【问题标题】:References in C++C++ 中的引用
【发布时间】:2010-08-17 13:08:05
【问题描述】:

刚入门C++,接触过参考资料,没完全看懂。

引用,正如我所读,是对象的替代名称。为什么使用它而不是直接访问对象,因为对引用的任何操作都会直接反映在对象上...?

  1. 为什么以及何时使用它们?
  2. ist 是不是像一个常量指针,每次使用时都会被引用...?

而且,它说

 double& dr = 1;  ----  says it is an error (some lavalue needed) 
 const double& cdr = 1;  ---- says it is ok. 

我没有正确理解它..所以请解释为什么会这样......

谢谢你...:)

【问题讨论】:

标签: c++


【解决方案1】:

为什么要使用它而不是直接使用 以任何操作访问对象 参考文献直接反映在 对象...?

C++ 按值传递参数,这意味着如果你有一个函数,例如:

void foo(MyObject o) { ... }

默认情况下,C++ 会复制MyObject直接使用传入的对象。因此,引用的一种用途是确保您处理的是同一个对象:

void foo(MyObject &o) { ...}

或者,如果您不修改o

void foo(const MyObject &o) { ... }

【讨论】:

    【解决方案2】:

    引用是最初在 C 代码中的另一种方式

    无效 fubarSquare(int *x){ 整数 y = *x; *x = y * y; } // 典型调用 诠释 z = 2; fubarSquare(&z); // 现在 z 是 4

    在 C++ 中的引用会是这样的

    无效 fubarSquareCpp(int& x){ x = x * x; } // 典型调用 诠释 z = 2; fubarSquareCpp(z); // 现在 z 是 4

    这是一种更简洁的语法方式,使用 call-by-reference 参数而不是使用 C 的符号星号/星号来指示指针并作为 call-by-reference em> 参数...和 ​​修改 直接在函数之外的参数...

    查看 Bjarne Stoustrap 的页面 here,其中介绍了 C++ 的原理以及技术常见问题解答here

    【讨论】:

      【解决方案3】:

      引用基本上是一个看起来像对象的指针。尽管您可以通过箍创建一个 NULL 引用,但很难获得一个 NULL 引用。

      关于您的示例, 1 是右值或结果。它只是一个临时变量,不能修改。因此,您不能对它进行非常量引用。但是,您可以对它进行 const 引用。这意味着您不能更改引用的值。

      这是一个创建 NULL 引用的示例。不要这样做!

      int * x = (int *)NULL;
      int & y = *x;
      

      【讨论】:

      • 如果不调用未定义的行为,就不可能获得“空引用”。
      • 并非不可能,如果您可能访问它,您将获得一个 seg-v。
      • 不可能不调用未定义的行为。在您的示例中,*x 是 NULL 取消引用,即 UB。
      • 是的,有些编译器可以精确优化,因为你不能有 NULL 引用。所以他们甚至可能不会费心执行你的第二条语句。
      • 我认为我的观点是它是可能的,并且这是编码人员应该注意的事情。您可能会在您不期望的地方看到一个 seg-v,而不是在您创建参考的地方。是的,这是未定义的行为,但我已经看到很多代码通过访问器方法将成员指针转换为 const 引用,因此值得知道当你的代码出错时你可能会遇到什么。
      【解决方案4】:

      我同意你的看法。仅使用引用作为别名并不是很有用。 如果您将其视为不可变指针,它会更有用。但实际上没那么有用。

      实际上,它用于定义干净的接口。例如当你定义:

      int foo(const int& param);
      

      你说param是foo中的只读参数。

      不要忘记您必须为引用赋值。

      请参阅参考资料中的C++ faqlite 了解更多信息

      my2c

      【讨论】:

      • 我同意。在对象接口级别,它与函数的用户创建了明确的合同。无效 foo(Object& o){}; foo 函数要求一个指向可以修改且不为 NULL 的对象的指针。
      【解决方案5】:

      引用改进了语法,因此不需要指针取消引用。 假设 Base 是一个可以派生自的类:

      void someFunction(Base b)
      {
          b.function();
          // b is a copy of what was passed - probably performance issues
          // possible unintended object slicing - you only get the Base part of it
          // no virtual function call
          // no changes to b visible outside the function
      }
      
      void someFunction(Base* b)
      {
          b->function();
          // a shortcut for (*b).function();
          // b is the same object that was passed to the function
          // possible virtual call
          // changes visible outside the function
      }
      
      void someFunction(Base& b)
      {
          b.function();
          // b is the same object that was passed to the function
          // possible virtual call
          // changes visible outside the function
      }
      

      引用就像常量指针(不是指向常量的指针 - 即您可以更改对象,但不能更改指向的内容)。 const 引用是一个引用,通过它你可以做可以在 const 对象上做的事情。

      引用也不错,因为不能有空引用

      【讨论】:

        【解决方案6】:

        仔细阅读wikipedia 文章。总而言之,引用是指针的更友好版本,通常用于将对象作为引用传递给函数,而不必担心空指针。

        举例说明:


        想想数字1 表示为一个变量。编译时,这个数字被放入内存的全局部分,程序可以引用,但修改。

        所以它的类型是:const int

        double &dr = 1 正在尝试将dr对双精度的引用)分配给const int 1。由于1 是一个常量,编译器将不允许您对它进行非常量引用。

        在第二行:

        const double &dr = 1 正在尝试分配dr对双精度的常量引用const int 1。这是可行的,因为 reference 也是 const,因此可以指向 const int

        编辑 const int 在分配前转换为 const double

        【讨论】:

        • 其实这不是真的。文字1int 类型的右值,而不是const int。您可以在这里看到不同之处:ideone.com/tzV6p。尽管f() 返回一个非常量A,但它不能绑定到引用。
        • 是的,在这种情况下,文字 1 是一个右值。但是在处理进入全局内存空间的文字时,它可以工作(参见ideone.com/aZnid
        • 不,在所有情况下,文字 1 都是 int 类型的右值。 1 的实际值不需要存储在内存中的任何位置,除非您明确创建一个左值来存储它,这就是您在链接程序中所做的。
        • 在这个具体的例子中,你的解释基本上已经足够好了。它给出了事物背后的直观推理。 1 显然是一个常数值,所以 int& i = 1; 不应该工作是有道理的。但在理解引用的更广泛概念中,它是一个重要的区别。 int f(); int& i = f(); 不起作用的原因不太明显。
        • @Dennis Zickefoose:顺便说一句,语言实际上已经改变了:int&& i=1 应该可以工作。
        【解决方案7】:

        引用是表示它们所引用的另一个对象的语言实体。非常量引用是左值,必须用左值初始化。它们可以像这样有用:

        int& x=condition ? array[1] : array[2];
        int& y=condition ? array[0] : array[3];
        x+=y;
        y=0;
        

        当用作函数参数时,它们告诉调用者他必须传递一个可能被函数写入的左值:

        void set1(int& x) { x=1; }
        
        int foo;
        set1(foo); // ok, foo is 1
        set1(foo+1); // not OK, not lvalue
        

        另一方面,常量引用可以绑定到右值。在函数参数中,它们通常用于避免过多的副本:

        void niceness(std::string s); // the string would be copied by its copy-ctor
        void niceness(const std::string& s); // the caller's string would be used
        

        请注意,这可能会或可能不会产生更快的代码。

        当在普通代码中使用 const 引用时,它们也可以绑定右值和as a special rule, they extend the lifetime of the object they are bound to。这是您在代码中看到的:

        const double& d=1; // OK, bind a rvalue to a const-ref
        double& d=1; // Bad, need lvalue
        

        所有引用都是多态的,就像指针一样:

        class A { virtual void f(); }
        class B : public A { void f(); }
        
        B b;
        A& ar=b;
        ar.f(); // calls B::f()
        

        所有的引用都是像指针一样的别名:

        int f(int& a, const int& b)
        {
          a=1;
          return b;
        }
        
        int x;
        f(x, 42); // ==42, foo=1
        x=42;
        f(x, x); // ==1 (not 42), foo=1
        

        【讨论】:

          【解决方案8】:
          double& dr = 1; // 1.0 would be more clear
          

          无效,因为 1 被视为 const double 类型,因此如果您想要引用该变量,您需要引用 const double 所以

          const double& dr = 1.0;
          

          是正确的。

          【讨论】:

            【解决方案9】:

            引用的实用性在将参数传递给函数的上下文中最为明显。

            即,

            int a;

            func 定义:void foo (int& param) {param = 1;}

            func 调用:foo(a);

            'param' 别名'a' 的方式是干净的,其意图很容易被此代码的读者以及编译器在内联引用所需的任何额外内存分配时优化掉。

            【讨论】:

              【解决方案10】:

              传递对函数的引用然后让函数使用该引用几乎就像传递一个指向函数的指针然后让函数取消对指针的引用。在许多情况下,机器代码实现是相同的。但是,存在一些差异,尤其是在函数内联扩展的情况下。如果变量通过引用传递给内联函数,编译器通常能够在扩展函数时替换变量本身——即使存储在机器寄存器中。相比之下,如果获取变量的地址并将其作为指向函数的指针传递,然后取消引用它,编译器不太可能找出优化,除非它不仅确定 - 至少对于一个特定的扩展函数——指针将始终指向该变量,而且指针不会在其他任何地方使用(如果指针在其他地方使用,则该变量不能保存在寄存器中)。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2016-07-06
                • 2013-08-16
                • 2014-12-21
                • 2016-08-18
                • 2011-02-11
                • 2012-01-14
                • 2010-09-27
                相关资源
                最近更新 更多