2

我有一个非常简单的类来处理回调。来电者要求经理回电。只要调用者持有回调对象,它就保持有效。然而,一旦对象死亡,它的析构函数会将 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's 功能背后的基本思想?

4

3 回答 3

3

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

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

以下是单一所有权逻辑的简要概述:

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

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

于 2013-08-18T11:29:29.567 回答
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;
};
于 2013-08-18T11:27:53.853 回答
0

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

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

于 2013-08-18T12:49:47.990 回答