我有一个非常简单的类来处理回调。呼叫者要求管理员进行回叫。只要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
功能背后的基本理念?
答案 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,只是将资源从一个窃取到另一个。