返回一个对象会调用它的析构函数吗?

时间:2013-08-18 10:55:56

标签: c++ pointers return destructor

我有一个非常简单的类来处理回调。呼叫者要求管理员进行回叫。只要Callback持有Callback对象,它就会保持有效。然而,一旦对象死亡,它的析构函数会将Manager内部的指针设置为NULL,因此它知道在下次遇到它时将其丢弃。

...或者至少,这是我追求的基本想法。

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

我的问题是,Manager::requestCallback()在将结构返回给它的调用者之前是否会调用Callback的析构函数?

如果是这种情况,是否有任何方法可以阻止这种情况,同时(或多或少)保持Callback功能背后的基本理念?

3 个答案:

答案 0 :(得分:3)

堆栈中的每个对象在超出范围时会自动销毁。也就是说,从概念上讲,临时对象在从函数返回时将超出范围。也就是说,当返回一个复制对象时,编译器可能会省略它,在这种情况下,return语句中的临时对象似乎根本不存在。但是,如果复制elision发生与否,则是优化而不是保证。

仅仅取决于析构函数似乎不适用于您的情况。但是,可能有用的是区分传递的临时对象和被保持(命名或指向)的对象。基本思想是将指向的指针视为一个资源,该资源由一个对象拥有,并仅在实际所有者被销毁(类似于std::unique_ptr<T>)或所有所有者被销毁时重置它(类似于{{1 }})。假设您可以使用r值引用,则可以使两个表单都正确,否则您只能获得共享所有权。

以下简要介绍了单一所有权逻辑的外观:

std::shared_ptr<T>

如果你不能使用r值语义,你实际上仍然可以使用相同的逻辑,但是当创建副本时,存在“资源”被命名对象意外窃取的风险。使用移动构造函数可以避免这种风险。

答案 1 :(得分:2)

您可以添加移动构造函数(c ++ 11)或复制析构函数:

class Callback{
    friend Manager;
private:
    explicit Callback(Drawable** targetPtr) : drawablePtr(targetPtr) {}
    // C++11
    Callback(Callback&& rhs) : drawablePtr(rhs.drawablePtr) { rhs.drawablePtr = NULL; }
    // else
    Callback(Callback& rhs) : drawablePtr(rhs.drawablePtr) { rhs.drawablePtr = NULL; }
public:
    ~Callback(){
        if (drawablePtr) { (*drawablePtr) = NULL; }
    }
private:
    Drawable ** drawablePtr;
};

答案 2 :(得分:0)

好吧,在requestCallback返回后,临时Callback对象将被销毁,这将导致Drawable *设置为NULL,并且复制的Callback对象也不起作用。

移动确实有效,但它不是唯一的解决方案。事实上,这就像c ++ 98中的auto-ptr,只是将资源从一个窃取到另一个。