【问题标题】:pass by reference c++通过引用传递 C++
【发布时间】:2011-01-17 09:56:01
【问题描述】:

我的 C++ 老师告诉我,只有在我不打算更改函数内部数组的任何内容时,才应该使用引用调用。 我在我的程序中传递了一些非常大的向量。所有向量都将在函数内部进行修改。我的矩阵大小约为[256*256][256][50]...

这里有什么特别的理由不使用调用引用吗?

AFAIK 引用调用应该更快并且消耗更少的内存?

【问题讨论】:

  • 也许您的老师的意思是“仅当您不打算更改函数内的任何数据时才应使用传递 const 引用。”
  • 不,他没有。他告诉我们,如果您不打算更改任何内容,则仅使用引用调用是一个经验法则。我现在从下面的答案中看到,即使是老师也不总是现在他们在说什么。
  • 你是对的。老师们并不总是知道他们在说什么。有时他们确实知道,但不小心说错了。或者有时,他们说得对,而学生听错了。不要排除最后一个选项。
  • @jalf Heh,其实这一次,我可以排除最后一个选项,因为我可以在线阅读他的幻灯片,并且在其中找到了上述声明。然而这个论坛是关于编程的,所以让老师讨论另一个论坛......

标签: c++ pass-by-reference


【解决方案1】:

通过引用传递对象总是一个不错的选择,但我们需要小心,首先我们必须确定我们函数的目的/目的是什么?

你必须在这里做出选择,是只读取对象的数据还是修改它。

假设你有一个类似的界面

void increament_value(int& a);

所以在这里你可以修改我们正在传递的对象的值,但是当你传递敏感数据时这是一场灾难,你可能会丢失原始数据并且无法恢复它,对吧?

因此,c++ 为您提供了一种功能,即不更改您要传递给函数的引用的对象的值,并且传递对象的 const 引用始终是一个不错的选择,例如,

double get_discounted_amount(const double &amount,double discount){
    return (amount*discount/100);
}

这保证你的对象的实际值不会改变,但同样取决于你的界面的目的是你想改变它还是只使用(读取)它

【讨论】:

    【解决方案2】:

    除了所有关于何时以及如何为非原始类型传递可能的 const 引用的常见讨论之外,数组在这里非常特别。

    由于与 C 的向后兼容性,并且由于您的特定问题:数组可能很大,在 C 或 C++ 中,数组永远不会真正按值传递。该数组将衰减为指向第一个元素的指针,因此当您编写时:

    void foo( type array[100] );
    

    编译器实际上正在处理:

    void foo( type *array );
    

    不管数组的大小是多少(有两个常见的陷阱:相信arrayfoo 中的一个数组,并相信它会保证有100 个元素。

    现在,在 C++ 中,您实际上可以通过引用传递数组,但引用必须是数组的具体类型,包括大小:

    void foo_array( type (&array)[100] );
    

    那里有趣的语法告诉编译器该函数将采用一个包含 100 个 type 类型元素的数组。这样做的好处是编译器可以为您执行大小检查:

    // assuming 'type' is defined
    int main() {
       type array0[99];
       type array1[100];
    
       foo( array0 );     // compiles, but if size=100 is assumed it will probably break
                          // equivalent to: foo( &array0[0] )
       // foo2( array0 ); // will not compile, size is not 100
       foo2( array1 );    // compiles, size is guaranteed to be 100
    }
    

    现在,问题是您的函数仅适用于正好包含 100 个元素的数组,并且在某些情况下,您可能希望以不同的数组大小执行相同的操作。两种解决方案是:模板数组大小中的函数,它将为每个使用的大小提供大小安全的实现——更大的编译时间和二进制大小,模板针对每个不同的大小进行编译——或使用 pass-按值语法,这将使数组衰减——大小不安全,必须作为额外参数传递,减少编译时间和二进制大小。第三种选择是结合两者:

    void foo( type *array, int size );
    template <size_t N>
    void foo( type (&array)[N] ) {
       foo( array, N );
    }
    

    在这种情况下,虽然每个大小都会有一个模板化的foo,但编译器很可能会内联调用,并且生成的代码将等效于提供数组和大小的调用者。真实数组无需额外计算且类型安全。

    现在,传递引用很少用于数组。

    【讨论】:

    • 非常非常有用的答案,至少对我而言。我一直想知道如何创建这样的大小安全数组。凉爽的! (显然是+1)
    【解决方案3】:

    我们的风格是从不通过值传递对象,而是始终传递引用或 const 引用。不仅我们拥有可以包含 100 MB 数据并且按值传递的数据结构会成为应用程序杀手,而且如果我们按值传递 3D 点和向量,我们的应用程序也会停止运行。

    【讨论】:

      【解决方案4】:

      等一下.. 我害怕人们如何回答这个问题。据我所知,数组总是通过引用传递。

      void function(int array[])
      {
          std::cout << array[0] << '\n';
      }
      
      // somewhere else..
      int array[2] = { 1, 2 };
      function(array); // No copy happens here; it is passed by reference
      

      此外,您不能说数组参数是明确的引用,因为那将是创建引用数组的语法(这是不允许的)。

      void function(int &array[]) // error here
      { /* ... */ }
      

      那你是什么意思?

      此外,许多人说只有在修改函数内部数组的内容时才应该这样做。那么,对 const 的引用呢?

      void function(const int arr[])
      {
          std::cout << arr[0] << '\n';
      }
      

      --编辑

      请有人指出如何在 C++ 中通过引用传递数组?

      --编辑

      哦,你说的是向量。好的,那么经验法则是:

      • 仅当您要修改向量的内容时才通过引用传递。
      • 尽可能通过对 const 的引用传递。
      • 仅当所讨论的对象非常非常小(例如,包含整数的结构)或有意义时(我想不出一个案例),才通过值传递)。

      我错过了什么吗?

      --编辑

      • 对于普通 C 数组,当您想确保数组具有给定的确定大小时,最好通过引用传递它们(如在 void function(int (&amp;array)[100]) 中)。

      谢谢,dribeas。

      【讨论】:

      • 当我谈到数组时,这种困惑可能来自我的问题,但我实际上是指向量......
      • 它们不是通过引用传递,而是衰减为指向第一个元素的指针。编译器会将签名更改为void foo( int *array ),并将调用更改为foo( &amp;array[0] )。然后你再次可以通过引用传递一个数组:void foo( int (&amp;array)[100] )。语法有点晦涩,必须明确说明大小(因为它是类型的一部分),但你可以做到。
      【解决方案5】:

      一般来说,对象应该总是通过引用传递。否则会生成对象的副本,如果对象很大,则会影响性能。

      现在如果你调用的方法或函数没有修改对象,最好如下声明函数:

      void some_function(const some_object& o);
      

      如果您尝试在函数体内修改对象的状态,这将产生编译错误。

      另外需要注意的是,数组总是通过引用传递的。

      【讨论】:

        【解决方案6】:

        如果满足以下条件,您可以通过引用传递:

        1. 您不会修改传递的对象
        2. 您想修改对象并且不想保持旧对象不变

        当你通过引用传递某些东西时,只有指针被传递给函数。如果你传递整个对象,那么你需要复制它,所以它会消耗更多的 CPU 和内存。

        【讨论】:

          【解决方案7】:

          为防止意外更改,请使用 pass-by-const-reference;这样,默认情况下*,传入的数组不能被调用的函数更改。

          * 可以用const_cast 覆盖。

          【讨论】:

            【解决方案8】:

            我看不出您不能通过引用传递的任何原因。或者,您可以传递指针,但我认为有时通过引用传递更好,因为它可以避免空指针异常。

            如果您的老师建议将此作为某种约定,请在合理的情况下随意打破它。您可以随时在函数上方的评论中记录这一点。

            【讨论】:

              【解决方案9】:

              你的老师错了。如果你需要修改数组,通过引用传递是要走的路。如果您不想修改某些内容,请通过 const 引用传递。

              【讨论】:

              • 其实,也许“错”太苛刻了。也许“被误解”更好。
              【解决方案10】:

              通常,在介绍性课程中,他们会告诉您,这样您就不会意外更改您不想更改的内容。

              如果您通过引用传入 userName,然后不小心将其更改为 mrsbuxley,这可能会导致错误,或者至少在以后会造成混淆。

              【讨论】:

                【解决方案11】:

                我的 C++ 老师告诉我,只有在我不打算更改函数内部数组的任何内容时,才应该使用引用调用。

                它应该在你没有改变函数内部的东西时使用关于要反映在原始数组中的更改。

                如果您希望您的函数更改原始数组(您需要在调用后保留原始值)并且被调用函数更改了传递参数。

                【讨论】:

                • 它绝对应该用于更大的数组,即使你不想改变它。那时你通过 const 引用传递它,编译器会让你诚实。
                • @Xorlev:是的。我说别的了吗?我正在阅读我的答案,但我不太明白哪一部分可能会被误解。它在说同样的事情。
                猜你喜欢
                • 2020-04-01
                • 2014-03-10
                • 2012-06-14
                • 2014-05-05
                • 2012-01-27
                • 2020-12-06
                • 1970-01-01
                • 2018-09-01
                • 2020-07-30
                相关资源
                最近更新 更多