为什么我要使用引用变量?

时间:2012-10-04 13:51:36

标签: c++ pointers reference

作为我的第一门编程语言,我学习了Java,但是由于我改为另一所大学,我现在正在学习C ++。

来自Java并学习C ++的基础知识,我阅读了有关引用和引用变量的内容。它们有多危险,如何小心它们等等。

所以在我脑海里出现了一个简单的问题: 为什么我要打扰使用那种复杂的,因此可能导致问题的东西呢?

它是不是值得,或者只是RAM大约64MB的时候的遗物?

由于很多答案都提到了指针:这个概念显然来自石器时代,imho。除了高性能计算,我甚至不会触及那些东西。

8 个答案:

答案 0 :(得分:8)

问题与引用本身无关。

问题在于,在C ++中,对象生存期的管理方式与使用垃圾收集器的Java或其他运行时环境不同。 C ++没有标准的内置垃圾收集器。 C ++对象生存期可以是自动的(在本地或全局范围内)或手动(在堆中显式分配/解除分配)。

C ++引用只是对象的简单别名。它对对象的生命周期一无所知(为了提高效率)。程序员必须关心它。例外情况是引用绑定到临时对象的特殊情况;在这种情况下,临时的生命周期延长到绑定引用的生命周期。详细信息为here

引用是C ++基本概念的重要组成部分,您无法避免在90%的任务中使用它们。否则你必须使用指针,这通常更糟糕: - )

,例如,当您需要将对象作为函数参数通过引用而不是按值传递时,您可以使用引用:

void f(A copyOfObj);       // Passed by value, f receives copy of passed A instance
void f(A& refToObj);       // Passed by ref, f receives passed A instance itself, modifiable
void f(const A& refToObj); // Passed by const ref, f receives passed A instance itself, non modifiable

答案 1 :(得分:5)

引用是具有限制的指针。引用是访问对象的一种方式,但不是对象本身。如果您的代码使用这些限制是有意义的,那么使用引用而不是指针可以让编译器警告您不小心违反它们。

答案 2 :(得分:4)

如果按值传递大对象作为函数参数,可能会有一个非常真实的性能下降,通过将它们作为引用传递来解决。此外,如果通过引用而不是值传递函数,则可以修改函数内的对象。

答案 3 :(得分:3)

当你刚刚来自Java时,我可以想象你会遇到许多C / C ++让你做的危险的问题。在我看来,区别在于:

  • C / C ++允许您执行大量强大的危险内容。你可以做很多伟大的事情,但也可以拍自己的腿
  • Java 限制您可以在安全名称中执行的操作

你不喜欢什么(“指针/引用是危险的”)正是我喜欢的(“它让我做了很多事情,如果我搞砸了它是我的错”)关于C ++。在我看来,这是编程语言品味的问题。

参考是为了避免指针的一些危险,但另一方面,即使指针仍然有明显的快速访问的东西使用。例如,假设您有一个低级对象,它可以执行一些有用的工作,并在多个高级对象之间共享。它可以由于各种原因而共享(例如,它每次执行有用的工作时都会更新内部状态,因此复制它不是一种选择)。

class HL{
    private:
    LL &object;

    public:
    HL(LL &obj){
        object = obj; // initialization of reference
    }
    // a no-argument constructor is not possible because you HAVE to
    // initialize the reference at object-creation (or you could initialize
    // it to a dummy static object of type LL, but that's a mess)
    void some_routine(){
        ....
        object.useful_work();
        ...
    }
}

在这种情况下使用引用强制执行内部object的初始化。另一方面,如果对象为高级对象提供的功能只是可选,那么指针是一种可行的方法:

class HL{
    private:
    LL *object;

    public:
    HL(LL &obj){
        object = &obj; // initialization of reference
    }
    HL(){} // no-argument constructor possible
    void some_routine(){
        ....
        if (object != NULL)
           object->useful_work();
        ...
    }
}

另外,至于使用引用调用,当你将一个大结构传递给一个函数时,这是最有用的,例如: vector<int>

void function(vector <int> a); // call by value, vector copied every time
void function(vector <int> &a); // call by reference, you can modify
                                // the vector in the function
void function(const vector <int> &a); // if you don't want to be able to modify
                                      // it, you can add const for "protection"

答案 4 :(得分:2)

实际问题是“为什么使用引用而不是指针”。

我还没想出来。目前,我使用引用

  • 因为您无法分配给他们。最糟糕的情况是,这对编译器没有任何意义,但编译器有可能利用这些知识。

  • 通常当我想返回一个始终存在的指针,或者指示一个参数是必需的并且可能不是nullptr(没有空引用)时。这不是一个艰难的规则;有时一个实际的指针感觉更自然。

  • 当事情只是要求引用时,例如运算符重载。

一般来说,我最终使用了很多引用,因为我通常不想复制对象,除非我真的需要一个实际的副本,并且引用通常都有效。

答案 5 :(得分:1)

引用使引用调用更容易,因为调用者在传递对象时不必使用&运算符。

像这样:

MyObject ob;

void foo_ref(MyObject& o){ ...}
void foo_ptr(Myobject* o){ ...}

//call

foo_ref(ob);
foo_ptr(&ob);

此外,它们可以在函数中初始化指针:

MyObject* ob = nullptr;
MySubject* sub = nullptr;

void foo_init(MyObject *& o, MySubject *& s) {

   o = new MyObject;
   s = new MySubject;
}

//call

foo_init(ob, sub);

如果没有对指针的引用,那么这个例子只能用指向指针的指针,使得代码看起来很糟糕,因为你必须首先去掉每个参数

MyObject* ob = nullptr;
MySubject* sub = nullptr;

void foo_init(MyObject ** o, MySubject ** s) {

   *o = new MyObject;
   *s = new MySubject;
}

//call

foo_init(&ob, &sub);

答案 6 :(得分:1)

最常使用的引用是避免昂贵的深层复制; 默认情况下,C ++具有值语义,如果你写了类似的东西:

void f( std::vector<double> d );

每次调用f时都会复制整个向量。所以你写 类似的东西:

void f( std::vector<double> const& d );

一个特别重要的地方是定义复制构造函数。如果你要写:

MyType( MyType other );

,自编译器以来,你最终会得到无限递归 必须调用复制构造函数才能将参数复制到此构造函数。 (实际上,编译器只接受构造函数 它引用了复制构造函数,正是为了避免这种情况 问题)。

在构造函数之外,这实际上是一种优化,可能是 被认为是过早优化。然而,在实践中,差不多 通用约定是通过引用传递类类型, 而不是价值。

参考也可以用作输出参数;与指针相比, 他们的优势在于他们不能为空。

引用也可用于为类数据提供“窗口”。 经典的例子是operator[]std::vector的重载; 它返回一个引用,以便您可以修改,获取地址等。 数组中的实际元素。

除了作为参数和返回值之外,在上述情况下, 参考文献有点罕见。如果您使用一个作为类成员,为 例如,你必须意识到你不能给这个经典的课程 赋值语义。 (如果班级不这样做,这不是问题 支持赋值。)作为一个全局变量,它们可能被用来隐藏 所涉及的内容的实施,但这是非常的 罕见。作为一个局部变量,关于我所知道的唯一用途是缓存 返回引用的另一个表达式的结果:例如,if 我在短时间内多次访问someMap[aKey] 代码,写下来是有道理的:

ValueType& element = someMap[aKey];

一开始,然后使用element。 (注意,这只会使 感觉因为someMap[aKey]返回一个引用。)

答案 7 :(得分:0)

例如,因为它们迫使你进行初始化,这会降低它们的危险性