【问题标题】:C++: Argument Passing "passed by reference"C++:参数传递“通过引用”
【发布时间】:2016-01-22 06:54:07
【问题描述】:

我理解与任何其他变量一样,参数的类型决定了参数与其参数之间的交互。我的问题是,为什么要引用参数而不是为什么不引用参数的原因是什么?为什么有些函数参数是引用而有些不是?无法理解这样做的好处,有人可以解释一下吗?

【问题讨论】:

  • 创建一个包含 2gB 数据的向量。现在将它传递给一个函数......一百万次。我赞扬这个问题。通常它以“引用和指针不一样吗?”开头。
  • 那么总是引用参数有好处吗?或者这不是一件很聪明的事情?
  • 没有。有时,按值传递有明确的目的。它非常有情境。其中一些情况可能非常微妙。
  • 不是每个 C++ 新手都曾问过同样的问题数百万次吗?你为什么不能用谷歌搜索它?
  • 非常抱歉,我以为我正确使用了这个资源。我只是认为最好向可以向我解释的知识渊博的人询问,而不是多次阅读材料(特别是 C++ Primer 第五版)而不完全理解“为什么”这个问题。

标签: c++ function parameters reference


【解决方案1】:

存在通过引用传递的能力有两个原因:

  1. 修改函数参数的值
  2. 为了避免出于性能原因复制对象

修改参数示例

void get5and6(int *f, int *s)  // using pointers
{
    *f = 5;
    *s = 6;
}

这可以用作:

int f = 0, s = 0;
get5and6(&f,&s);     // f & s will now be 5 & 6

void get5and6(int &f, int &s)  // using references
{
    f = 5;
    s = 6;
}

这可以用作:

int f = 0, s = 0;
get5and6(f,s);     // f & s will now be 5 & 6

当我们通过引用传递时,我们传递变量的地址。通过引用传递类似于传递指针 - 在这两种情况下都只传递地址。

例如:

void SaveGame(GameState& gameState)
{
    gameState.update();
    gameState.saveToFile("save.sav");
}

GameState gs;
SaveGame(gs)

void SaveGame(GameState* gameState)
{
    gameState->update();
    gameState->saveToFile("save.sav");
}

GameState gs;
SaveGame(&gs);


由于只传递了地址,因此变量的值(对于大型对象来说可能非常大)不需要复制。因此,通过引用传递可以提高性能,尤其是在以下情况下:
  1. 传递给函数的对象很大(我会在这里使用指针变体,以便调用者知道函数可能会修改变量的值)
  2. 该函数可以被多次调用(例如,在循环中)

另外,请阅读 const 参考资料。使用时,不能在函数中修改参数。

【讨论】:

    【解决方案2】:

    This article helped me a lot.

    请暂时忘记指针。把这个和一粒盐一起吃。

    引用对象。当您通过引用传递时,您传递的是 对象。

    当你通过值传递时,你传递了一个对象的副本另一个对象。它可能有相同的状态,但它是一个不同的实例;一个克隆。

    因此,如果您满足以下条件,则通过引用传递可能是有意义的:

    • 需要修改函数内部的对象
    • 不需要(或不想)修改 对象,但希望避免复制它只是为了将其传递给函数。这将是一个const 参考。

    如果您满足以下条件,则按值传递可能是有意义的:

    • 想从一个相同的 双胞胎开始,不打扰原来的双胞胎
    • 不关心复制对象的成本(例如,我不会通过引用传递int除非我想修改它)。

    这里,看看这段代码:

    #include<iostream>
    
    struct Foo {
      Foo() { }
      void describe() const {
        std::cout<<"Foo at address "<<this<<std::endl;
      }  
    };
    
    void byvalue(Foo foo) {
      std::cout<<"called byvalue"<<std::endl;
      foo.describe();
    }
    
    void byreference(Foo& foo) {
      std::cout<<"called byreference"<<std::endl;  
      foo.describe();
    }
    
    int main() {
      Foo foo;
      std::cout<<"Original Foo"<<std::endl;
      foo.describe();
      byreference(foo);
      byvalue(foo);
    }
    

    然后像这样编译它:g++ example.cpp

    运行它:./a.out

    并检查输出(实际地址可能在您的计算机中有所不同,但要点仍然存在):

    Original Foo
    Foo at address 0x7fff65f77a0f
    called byreference
    Foo at address 0x7fff65f77a0f
    called byvalue
    Foo at address 0x7fff65f779f0
    

    注意called byreference 地址与Original Foo 地址相同(两者都是0x7fff65f77a0f)。请注意called byvalue 地址的不同(它是0x7fff65f779f0)。

    提高一个档次。修改代码如下:

    #include<iostream>
    #include<unistd.h> // for sleeping
    
    struct Foo {
      Foo() { }
      Foo(const Foo&) {
        sleep(10); // assume that building from a copy takes TEN seconds!
      }
      void describe() const {
        std::cout<<"Foo at address "<<this<<std::endl;
      }  
    };
    
    void byvalue(Foo foo) {
      std::cout<<"called byvalue"<<std::endl;
      foo.describe();
    }
    
    void byreference(Foo& foo) {
      std::cout<<"called byreference"<<std::endl;  
      foo.describe();
    }
    
    int main() {
      Foo foo;
      std::cout<<"Original Foo"<<std::endl;
      foo.describe();
      byreference(foo);
      byvalue(foo);  
    }
    

    以同样的方式编译它,并注意输出(cmets 不在输出中;为清楚起见包括在内):

    Original Foo
    Foo at address 0x7fff64d64a0e
    called byreference
    Foo at address 0x7fff64d64a0e # this point is reached "immediately"
    called byvalue # this point is reached TEN SECONDS later
    Foo at address 0x7fff64d64a0f
    

    因此,该代码旨在夸大副本的成本:当您通过引用调用时,不会产生此成本。当你按值调用时,你必须等待十秒钟。

    注意:我的代码是在 OS X 10.7.4 中使用 GCC 4.8.1 编译的。如果您在 Windows 中,您可能需要与 unitsd.h 不同的东西才能使 sleep 调用正常工作。

    也许这会有所帮助。

    【讨论】:

      【解决方案3】:

      使用按引用传递的优点: 您不必创建数据的副本,您只需在内存中传递指向它的指针。 (想想如果你有一个巨大的物体,你会获得巨大的性能胜利)。 您可以“返回”多个值 我知道 c/c++ 中的某些函数返回一个数字,其中一个参数是指向被操作数据的指针。

      使用按引用传递的缺点: 您必须小心修改传递的数据,因为它可能会导致您可能想要也可能不想要的副作用。

      【讨论】:

        【解决方案4】:

        通过引用同样是通过指针手动传递变量,但通过引用不会让用户处理“容易搞砸”的指针。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-01-04
          • 2010-10-07
          • 2011-10-07
          • 2016-05-13
          • 1970-01-01
          • 2011-08-21
          • 2017-09-25
          • 1970-01-01
          相关资源
          最近更新 更多