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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/752658/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:06:16  来源:igfitidea点击:

Is the practice of returning a C++ reference variable evil?

c++referencec++-faq

提问by Nick Bolton

This is a little subjective I think; I'm not sure if the opinion will be unanimous (I've seen a lot of code snippets where references are returned).

我认为这有点主观;我不确定意见是否一致(我看过很多返回引用的代码片段)。

According to a comment toward this question I just asked, regarding initializing references, returning a reference can be evil because, [as I understand] it makes it easier to miss deleting it, which can lead to memory leaks.

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

This worries me, as I have followed examples (unless I'm imagining things) and done this in a fair few places... Have I misunderstood? Is it evil? If so, just how evil?

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

I feel that because of my mixed bag of pointers and references, combined with the fact that I'm new to C++, and total confusion over what to use when, my applications must be memory leak hell...

我觉得由于我的指针和引用混杂在一起,再加上我是 C++ 新手,并且对何时使用什么完全困惑,我的应用程序一定是内存泄漏地狱......

Also, I understand that using smart/shared pointers is generally accepted as the best way to avoid memory leaks.

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

回答by GManNickG

In general, returning a reference is perfectly normal and happens all the time.

一般来说,返回引用是完全正常的,并且一直发生。

If you mean:

如果你的意思是:

int& getInt() {
    int i;
    return i;  // DON'T DO THIS.
}

That is all sorts of evil. The stack-allocated iwill go away and you are referring to nothing. This is also evil:

那是种种恶。分配的堆栈i将消失,而您什么也没指。这也是邪恶的:

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

Because now the client has to eventually do the strange:

因为现在客户端最终必须做奇怪的事情:

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

Note that rvalue references are still just references, so all the evil applications remain the same.

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

If you want to allocate something that lives beyond the scope of the function, use a smart pointer (or in general, a container):

如果你想分配一些超出函数范围的东西,使用智能指针(或者一般来说,一个容器):

std::unique_ptr<int> getInt() {
    return std::make_unique<int>(0);
}

And now the client stores a smart pointer:

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

std::unique_ptr<int> x = getInt();

References are also okay for accessing things where you know the lifetime is being kept open on a higher-level, e.g.:

引用也可以用于访问您知道生命周期在更高级别上保持打开状态的内容,例如:

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

    const int& get() const { return i_; }
private:
    int i_;
};

Here we know it's okay to return a reference to i_because whatever is calling us manages the lifetime of the class instance, so i_will live at least that long.

在这里我们知道返回一个引用是可以的,i_因为任何调用我们的东西都会管理类实例的生命周期,所以i_至少会存活那么长时间。

And of course, there's nothing wrong with just:

当然,只要:

int getInt() {
   return 0;
}

If the lifetime should be left up to the caller, and you're just computing the value.

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

Summary: it's okay to return a reference if the lifetime of the object won't end after the call.

总结:如果对象的生命周期在调用后没有结束,那么返回一个引用是可以的。

回答by Charlie Martin

No. No, no, a thousand times no.

不,不,不,一千次不。

What is evil is making a reference to a dynamically allocated object and losing the original pointer. When you newan object you assume an obligation to have a guaranteed delete.

邪恶的是引用动态分配的对象并丢失原始指针。当你new成为一个对象时,你就承担了一个有保证的义务delete

But have a look at, eg, operator<<: that mustreturn a reference, or

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

cout << "foo" << "bar" << "bletch" << endl ;

won't work.

不会工作。

回答by David Thornley

You should return a reference to an existing object that isn't going away immediately, and where you don't intend any transfer of ownership.

您应该返回对不会立即消失的现有对象的引用,并且您不打算在其中进行任何所有权转移。

Never return a reference to a local variable or some such, because it won't be there to be referenced.

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

You can return a reference to something independent of the function, which you don't expect the calling function to take the responsibility for deleting. This is the case for the typical operator[]function.

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

If you are creating something, you should return either a value or a pointer (regular or smart). You can return a value freely, since it's going into a variable or expression in the calling function. Never return a pointer to a local variable, since it will go away.

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

回答by thorhunter

I find the answers not satisfactory so I'll add my two cents.

我发现答案并不令人满意,所以我会加上我的两分钱。

Let's analyze the following cases:

我们来分析以下案例:

Erroneous usage

错误使用

int& getInt()
{
    int x = 4;
    return x;
}

This is obviously error

这显然是错误

int& x = getInt(); // will refer to garbage

Usage with static variables

与静态变量一起使用

int& getInt()
{
   static int x = 4;
   return x;
}

This is right, because static variables are existant throughout lifetime of a program.

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

int& x = getInt(); // valid reference, x = 4

This is also quite common when implementing Singleton pattern

这在实现单例模式时也很常见

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

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

}

Usage:

用法:

 Singleton& my_sing = Singleton::instance(); // Valid Singleton instance
 my_sing.printHello();  // "Hello"

Operators

运营商

Standard library containers depend heavily upon usage of operators which return reference, for example

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

T & operator*();

may be used in the following

可用于以下

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

Quick access to internal data

快速访问内部数据

There are times when & may be used for quick access to internal data

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

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

    public:
        std::vector<int>& data()
        {
             return m_data;
        }
}

with usage:

用法:

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

HOWEVER, this may lead to pitfall such as this:

然而,这可能会导致这样的陷阱:

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!

回答by Mehrdad Afshari

It's not evil. Like many things in C++, it's good if used correctly, but there are many pitfalls you should be aware of when using it (like returning a reference to a local variable).

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

There are good things that can be achieved with it (like map[name] = "hello world")

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

回答by John Dibling

"returning a reference is evil because, simply [as I understand] it makes it easier to miss deleting it"

“返回一个引用是邪恶的,因为,简单地[据我所知]它更容易错过删除它”

Not true. Returning a reference does not imply ownership semantics. That is, just because you do this:

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

Value& v = thing->getTheValue();

...does not mean you now own the memory referred to by v;

...并不意味着您现在拥有 v 所指的内存;

However, this is horrible code:

然而,这是可怕的代码:

int& getTheValue()
{
   return *new int;
}

If you are doing something like this because "you don't require a pointer on that instance"then: 1) just dereference the pointer if you need a reference, and 2) you will eventually need the pointer, because you have to match a new with a delete, and you need a pointer to call delete.

如果你做这样的事情是因为“你不需要那个实例上的指针”,那么:1)如果你需要一个引用,只需取消引用指针,2)你最终会需要这个指针,因为你必须匹配一个new 带有删除,你需要一个指针来调用删除。

回答by John Dibling

There are two cases:

有两种情况:

  • const reference --good idea, sometimes, especially for heavy objects or proxy classes, compiler optimization

  • non-const reference --bad idea, sometimes, breaks encapsulations

  • const 引用——好主意,有时,特别是对于重对象或代理类,编译器优化

  • 非常量引用——坏主意,有时会破坏封装

Both share same issue -- can potentially point to destroyed object...

两者都有相同的问题——可能指向被破坏的对象......

I would recommend using smart pointers for many situations where you require to return a reference/pointer.

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

Also, note the following:

另外,请注意以下事项:

There is a formal rule - the C++ Standard (section 13.3.3.1.4 if you are interested) states that a temporary can only be bound to a const reference - if you try to use a non-const reference the compiler must flag this as an error.

有一个正式的规则——C++ 标准(如果你感兴趣的话,第 13.3.3.1.4 节)规定,临时变量只能绑定到一个常量引用——如果你尝试使用非常量引用,编译器必须将其标记为一个错误。

回答by John Dibling

Not only is it not evil, it is sometimes essential. For example, it would be impossible to implement the [] operator of std::vector without using a reference return value.

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

回答by AMA

Addition to the accepted answer:

除了已接受的答案:

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

    const int& get() const { return i_; }
private:
    int i_;
};
struct immutableint {
    immutableint(int i) : i_(i) {}

    const int& get() const { return i_; }
private:
    int i_;
};

I'd argue that this example is not okayand should be avoided if possible. Why? It is very easy to end-up with a dangling reference.

我认为这个例子不好,应该尽可能避免。为什么?很容易以dangling reference 结束

To illustrate the point with an example:

举例说明这一点:

struct Foo
{
    Foo(int i = 42) : boo_(i) {}
    immutableint boo()
    {
        return boo_;
    }  
private:
    immutableint boo_;
};

entering the danger-zone:

进入危险区:

Foo foo;
const int& dangling = foo.boo().get(); // dangling reference!

回答by MinandLucy

return reference is usually used in operator overloading in C++ for large Object, because returning a value need copy operation.(in perator overloading, we usually don't use pointer as return value)

返回引用通常用于C++中大对象的运算符重载,因为返回值需要复制操作。(在运算符重载中,我们通常不使用指针作为返回值)

But return reference may cause memory allocation problem. Because a reference to the result will be passed out of the function as a reference to the return value, the return value cannot be an automatic variable.

但是返回引用可能会导致内存分配问题。因为对结果的引用会作为对返回值的引用传出函数,所以返回值不能是自动变量。

if you want use returning refernce, you may use a buffer of static object. for example

如果你想使用返回引用,你可以使用静态对象的缓冲区。例如

const max_tmp=5; 
Obj& get_tmp()
{
 static int buf=0;
 static Obj Buf[max_tmp];
  if(buf==max_tmp) buf=0;
  return Buf[buf++];
}
Obj& operator+(const Obj& o1, const Obj& o1)
{
 Obj& res=get_tmp();
 // +operation
  return res;
 }

in this way, you could use returning reference safely.

这样,您就可以安全地使用返回引用。

But you could always use pointer instead of reference for returning value in functiong.

但是你总是可以使用指针而不是引用来返回函数中的值。