按值传递/指针传递/引用澄清

Pass By Value/Pointer/Reference Clarification

本文关键字:引用 指针 按值传递      更新时间:2023-10-16

我需要一次彻底澄清关于传递值/指针/引用的问题。

如果我有一个变量,比如

int SomeInt = 10;

我想把它传递给一个函数,比如

void DoSomething(int Integer)
{
    Integer = 1;
}

在我目前的情况下,当传递SomeInt到DoSomething()我希望SomeInt的值是基于我们做的DoSomething()以及最有效的内存和性能更新,所以我没有复制变量周围?话虽如此,下面哪个原型可以完成这个任务?

void DoSomething(int* Integer);
void DoSomething(int& Integer);

如何将变量传递到函数中?前两个原型有什么不同?

最后如果在类中使用函数

class SomeClass
{
    int MyInteger;
public:
    void ChangeValue(int& NewValue)
    {
        MyInteger = NewValue;
    }
};

如果我传递一个整数到ChangeValue,当我在get的删除中传递的整数将意味着当我试图从类内使用MyInteger它将不再是可用的?

感谢大家的时间,我知道这是一个基本的问题,但是我不断遇到的解释使我更加困惑。

从功能上讲,这三种方法都有效:

  • 传递一个int改变返回类型int,这样你可以返回新的值,用法:x = f(x);

    • 当你计划设置值而不需要读取初始值时,最好使用像int DoSomething();这样的函数,这样调用者就可以只说int x = f();,而不必在较早的行上创建x,并且想知道/担心它是否需要在调用之前初始化为任何东西。
  • 传递int&并在函数内设置,用法:int x; x = ? /* if an input */; f(x);

  • 传递int*并在函数内部设置指向int,用法:int x; x = ?; f(&x);

在内存和性能上是最有效的,所以我没有复制

周围的变量

考虑到c++标准并没有规定编译器应该如何实现引用,试图推断它们的特征是有点可疑的——如果你想把代码编译成汇编代码或机器码,看看它在你的特定编译器上是如何工作的(对于特定的编译器命令行选项等)。如果您需要一个经验法则,假设引用与指针具有相同的性能特征,除非概要分析或生成代码检查有不同的建议。

对于int,你可以期望上面的第一个版本不会比指针版本慢,可能更快,因为int参数可以在寄存器中传递和返回,而不需要内存地址。

如果/当/在指针传递版本是内联有更多的机会,潜在的缓慢"需要一个内存地址,所以我们可以传递一个指针"/"必须解引用一个指针来访问/更新值"方面的指针传递版本可以优化出来(如果你已经要求编译器尝试),留下两个版本具有相同的性能....

仍然,如果你需要问这样的问题,我无法想象你正在编写代码,这些都是重要的优化选择,所以一个更好的目标是做什么给你最干净,最直观和健壮的客户端代码使用…现在-无论是x = f(x);(你可能会忘记领先的x =),还是f(x),你可能没有意识到x可以修改,或者f(&x)(一些调用者可能认为他们可以通过nullptr本身就是一个合理的问题,但与你的性能问题分开。在这种情况下,c++ FAQ Lite推荐引用而不是指针,但我个人拒绝它的推理和结论——这一切都归结为对这两种惯例的熟悉程度,以及你需要传递const指针值的频率,或者nullptr是有效的指针值,这可能会与你的场景中希望的"你可以修改我"的含义混淆……这在很大程度上取决于你的编码风格、你使用的库、问题领域等。

你的两个例子

void DoSomething(int* Integer);
void DoSomething(int& Integer);

将完成任务。在第一种情况下-使用指针-您需要使用DoSomething(&SomeInt);调用函数,在第二种情况下-使用引用-更简单,如DoSomething(SomeInt);

推荐的方法是只要引用足够就使用引用,而指针只在必要时使用。

两种都可以。第一个原型的函数调用将是

DoSomething(&SomeInt);  

和第二个原型

DoSomething(SomeInt);  

如前所述,您可以同时使用这两种方法。

的优点
void DoSomething(int* Integer)
{
  *Integer=0xDEADBEEF;
}
DoSomething(&myvariable);

模式是,从调用中可以明显地看出myvariable可能会发生变化。

void DoSomething(int& Integer)
{
  Integer=0xDEADBEEF;
}
DoSomething(myvariable);

模式的优点是DoSomething中的代码更简洁,DoSomething很难以不好的方式扰乱内存,你可能会从中得到更好的代码。缺点是从读取调用中不能立即看出myvariable可能被更改。