返回C++引用变量的做法是邪恶的吗?

Nic*_*ton 325 c++ reference c++-faq

我觉得这有点主观; 我不确定这个意见是否会一致(我已经看过很多代码片段,其中返回了引用).

根据对这个问题的评论我刚刚问过,关于初始化引用,返回引用可能是邪恶的,因为,[据我所知]它更容易错过删除它,这可能导致内存泄漏.

这让我很担心,因为我跟随了一些例子(除非我想象的事情)并且在相当多的地方做到了这一点......我误解了吗?这是邪恶的吗?如果是这样,那有多邪恶?

我觉得因为我的指针和引用混合在一起,再加上我是C++的新手,以及对什么时候使用的完全混淆,我的应用程序必须是内存泄漏地狱......

另外,我知道使用智能/共享指针通常被认为是避免内存泄漏的最佳方法.

GMa*_*ckG 395

通常,返回引用是完全正常的并且始终发生.

如果你的意思是:

int& getInt() {
    int i;
    return i;  // DON'T DO THIS.
}
Run Code Online (Sandbox Code Playgroud)

这就是种种邪恶.分配的堆栈i将消失,你指的是什么.这也是邪恶的:

int& getInt() {
    int* i = new int;
    return *i;  // DON'T DO THIS.
}
Run Code Online (Sandbox Code Playgroud)

因为现在客户端最终必须做到这一点:

int& myInt = getInt(); // note the &, we cannot lose this reference!
delete &myInt;         // must delete...totally weird and  evil

int oops = getInt(); 
delete &oops; // undefined behavior, we're wrongly deleting a copy, not the original
Run Code Online (Sandbox Code Playgroud)

请注意,右值引用仍然只是引用,因此所有恶意应用程序保持不变.

如果要分配超出函数范围的内容,请使用智能指针(或通常是容器):

std::unique_ptr<int> getInt() {
    return std::make_unique<int>(0);
}
Run Code Online (Sandbox Code Playgroud)

现在客户端存储一个智能指针:

std::unique_ptr<int> x = getInt();
Run Code Online (Sandbox Code Playgroud)

参考也可以访问您知道生命周期在更高级别保持打开的内容,例如:

struct immutableint {
    immutableint(int i) : i_(i) {}

    const int& get() const { return i_; }
private:
    int i_;
};
Run Code Online (Sandbox Code Playgroud)

在这里我们知道可以返回一个引用,i_因为无论调用我们管理类实例的生命周期,所以i_至少会存活那么久.

当然,只是:没有错:

int getInt() {
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果生命周期应该留给调用者,而你只是计算值.

简介:如果对象的生命周期不会在调用后结束,则可以返回引用.

  • 为了后人的缘故,对于任何新的程序员来说,**指针都不错**.动态内存指针也不好.他们都有自己在C++中的合法位置.智能指针绝对应该是动态内存管理的默认设置,但是你的默认智能指针应该是unique_ptr,而不是shared_ptr. (165认同)
  • 这些都是不好的例子.正确使用的最佳示例是当您返回对传入的对象的引用时.ala operator << (20认同)
  • 编辑审批人:如果您无法保证其正确性,请不要批准编辑.我已经回滚了错误的编辑. (11认同)
  • 为了后人的缘故,对于任何新的程序员来说,**不要写`return new int`**. (6认同)
  • 为了后代,以及任何新的程序员,只需从函数中返回T. RVO将照顾一切. (3认同)
  • 难道“immutableint”示例也很危险吗,因为即使在“immutableint”被破坏之后,调用者也可能保留“int&amp;”?常见风格指南中是否推荐/建议不要使用这种模式? (3认同)
  • 我发现返回引用有用的唯一一次是链接函数调用.示例:`Class&Class :: translate(...){...; return*this}; Class&Class :: rotate(...){...; return*this;}; 类obj; .obj.translate(...)旋转(...)翻译(...);`. (2认同)

Cha*_*tin 63

不,不,一千次没有.

什么是邪恶的是引用动态分配的对象并丢失原始指针.当您new成为对象时,您有义务获得保证delete.

但是看看,例如operator<<:必须返回引用,或者

cout << "foo" << "bar" << "bletch" << endl ;
Run Code Online (Sandbox Code Playgroud)

不行.

  • 我贬低了因为这既没有回答问题(OP明确表示他知道删除的必要性),也没有解决返回对freestore对象的引用可能导致混淆的合理担心.叹. (23认同)
  • 返回引用对象的做法是*not*evil.Ergo没有.正如我在第二个格拉夫中指出的那样,他表达的恐惧是正确的恐惧. (4认同)
  • Iraimbilanja @关于"不" - 我不在乎.但这篇文章指出了GMan遗漏的一个重要信息. (2认同)

Dav*_*ley 46

您应该返回对不会立即消失的现有对象的引用,以及您不打算进行任何所有权转移的对象.

永远不要返回对局部变量或类似变量的引用,因为它不会被引用.

您可以返回对函数独立的引用,您不希望调用函数负责删除.这是典型operator[]功能的情况.

如果要创建某些内容,则应返回值或指针(常规或智能).您可以自由地返回一个值,因为它将进入调用函数中的变量或表达式.永远不要返回指向局部变量的指针,因为它会消失.

  • @David:当函数的返回类型为"T const&"时,实际发生的是return语句**隐式地将**类型为T的temp转换为按照6.6.3.2类型的"T const&"(a合法转换,但不延长生命周期),然后调用代码使用函数的结果初始化类型为"T const&"的ref,同样也是"T const&"类型 - 再次,合法但非生命周期延伸的过程.最终结果:没有延长寿命,而且很多混乱.:( (3认同)
  • @Mark:是的,确实有一些奇怪的规则.暂时的生命周期只能通过初始化const引用(不是类成员)来扩展; 它会一直存在,直到裁判超出范围.可悲的是,返回一个常规引用​​并没有被覆盖.然而,按值返回temp是安全的. (2认同)

tho*_*ter 20

我发现答案不尽如人意,所以我会加两分钱.

我们来分析下列情况:

错误的使用

int& getInt()
{
    int x = 4;
    return x;
}
Run Code Online (Sandbox Code Playgroud)

这显然是错误的

int& x = getInt(); // will refer to garbage
Run Code Online (Sandbox Code Playgroud)

用于静态变量

int& getInt()
{
   static int x = 4;
   return x;
}
Run Code Online (Sandbox Code Playgroud)

这是对的,因为静态变量在程序的整个生命周期中都是存在的.

int& x = getInt(); // valid reference, x = 4
Run Code Online (Sandbox Code Playgroud)

这在实现Singleton模式时也很常见

Class Singleton
{
    public:
        static Singleton& instance()
        {
            static Singleton instance;
            return instance;
        };

        void printHello()
        {
             printf("Hello");
        };

}
Run Code Online (Sandbox Code Playgroud)

用法:

 Singleton& my_sing = Singleton::instance(); // Valid Singleton instance
 my_sing.printHello();  // "Hello"
Run Code Online (Sandbox Code Playgroud)

运营商

例如,标准库容器严重依赖于返回引用的运算符的使用

T & operator*();
Run Code Online (Sandbox Code Playgroud)

可以在下面使用

std::vector<int> x = {1, 2, 3}; // create vector with 3 elements
std::vector<int>::iterator iter = x.begin(); // iterator points to first element (1)
*iter = 2; // modify first element, x = {2, 2, 3} now
Run Code Online (Sandbox Code Playgroud)

快速访问内部数据

有时可以使用&可以快速访问内部数据

Class Container
{
    private:
        std::vector<int> m_data;

    public:
        std::vector<int>& data()
        {
             return m_data;
        }
}
Run Code Online (Sandbox Code Playgroud)

用法:

Container cont;
cont.data().push_back(1); // appends element to std::vector<int>
cont.data()[0] // 1
Run Code Online (Sandbox Code Playgroud)

但是,这可能导致诸如此类的陷阱:

Container* cont = new Container;
std::vector<int>& cont_data = cont->data();
cont_data.push_back(1);
delete cont; // This is bad, because we still have a dangling reference to its internal data!
cont_data[0]; // dangling reference!
Run Code Online (Sandbox Code Playgroud)


Meh*_*ari 14

这不是邪恶的.像C++中的许多东西一样,如果正确使用它会很好,但是在使用它时你应该注意许多陷阱(比如返回对局部变量的引用).

用它可以实现好东西(比如map [name] ="hello world")

  • @wrongusername语法很直观.曾经试图在Java中增加存储在`HashMap <String,Integer>`中的值的计数?:P (5认同)

Joh*_*ing 10

"返回引用是邪恶的,因为,根据我的理解,它更容易错过删除它"

不对.返回引用并不意味着所有权语义.也就是说,仅仅因为你这样做:

Value& v = thing->getTheValue();
Run Code Online (Sandbox Code Playgroud)

......并不意味着你现在拥有v所指的记忆;

但是,这是一个可怕的代码:

int& getTheValue()
{
   return *new int;
}
Run Code Online (Sandbox Code Playgroud)

如果你正在做这样的事情,因为"你不需要在该实例上使用指针",那么:1)如果需要引用,只需取消引用指针,2)你最终需要指针,因为你必须匹配一个带删除的new,你需要一个指向调用delete的指针.


小智 7

有两种情况:

  • const引用 - 很好的想法,有时候,特别是对于重型对象或代理类,编译器优化

  • 非常规引用 - 有时会破坏封装

两者都有相同的问题 - 可能潜在地指向被破坏的对象......

我建议在需要返回引用/指针的许多情况下使用智能指针.

另请注意以下事项:

有一个正式的规则 - C++标准(如果你感兴趣,请参见第13.3.3.1.4节)声明临时只能绑定到const引用 - 如果你尝试使用非const引用,编译器必须将其标记为一个错误.


小智 5

它不仅不是邪恶的,而且有时是必要的。例如,如果不使用引用返回值,就不可能实现 std::vector 的 [] 运算符。