何时使用引用?

5
我的理解是引用是对已经存在的变量的别名,它们不能为null。当修改原始输入时,它们对于函数调用非常有用,因为相比指针而言,引用更安全。
除了上述情况和必须使用引用的情况外,还有其他原因/用例可以使用引用而不是所谓的“已经存在的变量”吗?
注:我想知道在不调用函数(我已经知道了)的情况下,引用还有哪些其他用途或用例?我还特别想知道在什么情况下引用优于原始变量,而不是优于指针(在选择两者之间时,另一个问题已经告诉我“尽可能使用引用,必须使用指针”)。

如果您想向函数或方法传递大型参数,或者想在对象中“存储”它(而不制作副本),请使用引用。引用具有与指针相同的功能(关于多态性或rtti)。没有办法指出什么是“更好”或“更差”的 - 引用或指针,一切都取决于您使用它们的上下文。 - user1770426
3
ITT:楼主问“除了在函数调用中,我们还在哪里使用引用”,而大家的回答都是“在函数调用中”。 - M.M
答案的一小部分:引用通常作为对象的不同类型别名而有用。if (x->is_a_foo()) { foo& xf=static_cast<foo&>(*x); xf.memberof_foo(...)...} - JSF
另一个答案的微小部分:当一个对象的一小部分混乱选择会以过大或明显错误的方式重复时:T& r=a[++i].b[--j].m_t; ... r(a)+r(b) ... 假设适当的 T::operator(),我们希望有一种不混乱的方法来调用它两次(而且不会两次修改 i 或 j)。 - JSF
4个回答

5

假设您有一个很大的结构,或者是std::vectorstd::string对象,并且您通过值将其传递给函数。这意味着该对象被复制,对于大型对象(例如数百万个条目的向量)可能相当低效。然后,您可以使用对常量对象的引用来传递,例如 std::vector<SomeType> const& my_object


3

0

尽可能使用引用而不是指针。除非您想操作低级内存,否则不应处理指针。另一个用途是使用new作为指针在堆上保留主数据,并传递该指针的引用。但是,使用new unique_ptr、shared_ptr和weak_ptr组合甚至都不需要这样做。唯一需要指针的地方是当您的对象可以为nullptr时,因为引用不能为NULL。您可以安全地假设引用是有效的对象,除非有人以变态的方式扭曲了代码。


0

普遍认为引用比指针更安全,但这是一种误解。产生空引用和其他非法引用同样容易。这些问题可能会在程序崩溃之前传播相当远。以下是一些例子:

  • 创建空引用:只需取消引用空指针以将结果传递给期望引用的内容。

    class Foo {
        public:
            Foo() : bar(null_ptr) {}
            void setSize(size_t newSize) {
                delete bar;
                bar = new int[newSize];
            }
            doSomething() {
                myVec.push_back(bar[0]);    //这将在push_back()实现的某个地方崩溃,而不是在此处!
            }
        private:
            int* bar;
            std::vector<int> myVec;
    };
    
    void baz() {
        Foo myFoo;
        myFoo.doSomething();
    }
    

    重点是上面的代码不会在您取消引用空指针的地方崩溃,它只会悄悄地创建一个空引用。这是合法的,因为将空指针取消引用被定义为未定义行为,并且创建空引用是编译器可以执行的有效操作。

    空引用将成功传递给std::vector<>::push_back(),它仅在它自己尝试使用引用实际获取其后面的值时才会导致段错误。

  • 悬空引用:让引用对象在引用结束之前结束其生命周期即可。同样,与指针相同的问题:

    std::vector<int> foo;
    foo->push_back(7);
    int& bar = foo[0];                   //完全合法
    bar *= 6;                            //完全合法
    foo->push_back(8);                   //完全合法
    cout << "The answer is: " << bar;    //BOOM,bar是悬空的,导致未定义行为的结果
    

    注意:此代码实际上可能按预期运行,就像访问悬空指针的代码可能可重复地按预期运行一样。

  • 超出范围的引用:与指针一样容易实现:

    std::vector<int> array;
    array.resize(7);
    int& bar = array[7];    //未定义行为,即使编译器检测到这一点,它也会悄悄地忽略它
    bar = 42;    //由于这是未定义行为,编译器可能会优化掉这一行
    

    与悬空引用一样,此代码可能完全正常运行,但可能不会执行您认为它执行的操作。由于优化,它甚至可能不会写入超出范围的元素。或者它可能会破坏堆栈,或者两者都会默默地计算错误的结果。在这里只是崩溃并不是最不可能的结果。

正如您所看到的,最后两个示例代码甚至不需要显式指针,它们仅使用现代、闪亮的C++风格数据结构。然而,它们会导致未定义的行为,在调用它的地方不会引起注意。非法引用可能从创建它的地方传播任何距离,然后才会开始发生明显的糟糕事情。这正是人们害怕使用指针的原因;指针和引用的危险是相同的。

因此,真正有意义的地方使用引用。但不要依赖于它们是“安全”的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接