Advertisement
Advertisement


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


Question

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...

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

2018/08/30
1
348
8/30/2018 8:13:47 AM

Accepted Answer

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 i will go away and you are referring to nothing. This is also evil:

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.

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.

2016/10/10
419
10/10/2016 7:31:43 AM

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 new an object you assume an obligation to have a guaranteed delete.

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

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

won't work.

2009/04/15

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.

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.

2014/05/07

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!
2017/02/27

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).

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

2009/04/15

"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;

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.

2017/05/23

Source: https://stackoverflow.com/questions/752658
Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Email: [email protected]