Search code examples
c++pointersreturndestructor

Will returning an object call its destructor?


I have a very simple class which handles callbacks. A caller asks for a Callback from a Manager. So long as the Callback object is held by the caller, it remains valid. Once the object dies however, it's destructor sets a pointer inside the Manager to NULL so it knows to throw it away when it next encounters it.

... or at least, that's the basic idea I'm after.

class Manager{
public:
    void executeOnList() { ... }

    Callback requestCallback(Drawable * target){
          Drawable ** ptr = list.add(target);
          return Callback(ptr);  // <-- the point of interest
    }
private:
    List list;
};


class Callback{
    friend Manager;
private:
    Callback(Drawable ** targetPtr){
        drawablePtr = targetPtr;
    }
public:
    ~Callback(){
        (*drawablePtr) = NULL;  // <-- dtor of interest
    }
private:
    Drawable ** drawablePtr;
};

My question is, will that Manager::requestCallback() call the destructor of Callback before returning the structure to it's caller?

If that is the case, would there be any way to prevent this, while (more or less) maintaining the basic idea behind Callback's functionality?


Solution

  • Every object which is on the stack gets destroyed automatically when it goes out of scope. That is, conceptually, the temporary object will get out of scope when it is returned from a function. That said, when returning an object copying it may be elided by the compiler, in which case the temporary object in the return statement doesn't seem to exist at all. However, if copy elision takes place or not is an optimization and not guaranteed.

    Solely depending on the destructor doesn't seem to work in your case. However, what may work is to distinguish between temporary objects being passed around and objects being held (named or pointed to). The basic idea would be to consider the pointed to pointer a resource which is owned by an object and resetting it only when the actual owner is destroyed (similar to std::unique_ptr<T>) or when all owners are destroyed (similar to std::shared_ptr<T>). Assuming you can use r-value references, you can get both forms right, otherwise you can only get the shared ownership right.

    Here is a brief outline of how the single ownership logic could look like:

    class Callback{
        friend Manager;
    private:
        Callback(Drawable ** targetPtr){
            drawablePtr = targetPtr;
        }
    public:
        Callback(Callback&& other):
            drawablePtr(other.drawablePtr) {
            other.drawablePtr = 0;
        }
        ~Callback(){
            if (drawablePtr) {
                (*drawablePtr) = 0;
            }
        }
    private:
        Drawable ** drawablePtr;
    };
    

    If you can't use r-value semantics you could still use the same logic, actually, but there is a risk that the "resource" is accidentally stolen by from a named object when a copy is created. Using a move constructor avoids this risk.