我遇到的情况是,同时访问容器的分配器需要同时调用new放置和析构函数。
template< class U > void destroy(U* p){
p->~U();
}
因为我最终可能会反复呼吁破坏。
这使我开始思考这样的事情是否应该可以。
std::vector<int>* p = (std::vector<int>*)malloc(sizeof(std::vector<int>));
::new (*p) std::vector<int>(30);
(*p)[10] = 5;
p->~std::vector<int>();
p->~std::vector<int>();
free(p);
我认为,只要销毁
std::vector
将数据指针设置为null或将大小设置为零,并且再次调用该函数时,就不会出现double free的情况。因此,是否应该进行分类以使意外(或良性)双重破坏等同于单一破坏?
换句话说,破坏应该是例如幂等的运算吗?
(为简单起见,在此示例中,析构函数不是虚拟的)
我发现问题类似于如何允许稍后移动的类被销毁的问题。
一些答案使运行时成本增加了支持双重破坏的理由。
有成本,但是它们与移动物体的成本相似。
换句话说,如果移动便宜,则允许双重销毁(如果DD由于其他原因(例如,标准)首先不是UB,则便宜)。
再具体一点:
class dummyvector{
int* ptr;
public:
dummyvector() : ptr(new int[10]){}
dummyvector(dummyvector const& other) = delete; // for simplicity
dummyvector(dummyvector&& other) : ptr(other.ptr){
other.ptr = nullptr; // this makes the moved object unusable (e.g. set) but still destructable
}
dummyvector& operator=(dummyvector const&) = delete; // for simplicity
void set(int val){for(int i = 0; i != 10; ++i) ptr[i]=val;}
int sum(){int ret = 0; for(int i = 0; i != 10; ++i) ret += ptr[i]; return ret;}
~dummyvector(){
delete[] ptr;
ptr = nullptr; // this is the only extra cost for allowing double free (if it was not UB)
// ^^ this line commented would be fine in general but not for double free (not even in principle)
}
};
最佳答案
鉴于销毁意味着对象生命周期的尽头,处理双重销毁会付出一定的代价(使内存处于可重新构图状态的额外工作),而好处是格式良好的代码永远都不会遇到。
说双重毁灭还可以,就说免费使用就可以了。当然,在特定情况下允许它的分配器可能会使错误代码保持正常工作,但这并不意味着如果它阻止分配器在非病理情况下有效且正确地工作,则该功能值得保留在分配器中。
如果您曾经处于可能发生双重破坏的位置,那么您可能已经处于可能会在销毁之后使用的位置,现在您必须对类中的每个操作进行检查和“处理”。 (这意味着)在已销毁实例上被调用的可能性。您已经妥协了正常操作以允许滥用,这是一条糟糕的开始之路。
简短版:避免双重破坏;当您被两次破坏时,有问题的代码无论如何都进入了未定义的行为区域,处理它不是您的工作。首先编写不会做糟糕事情的代码。
关于c++ - 类(class)是否应该具有抗灾能力,可以加倍破坏?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48533939/