如何在没有智能指针的情况下防止双重删除?

时间:2016-07-15 13:03:34

标签: c++ delete-operator

也许这是一个愚蠢的问题,但我只是想确保不要弄乱东西。假设我有这个结构:

struct Foo{
    struct Bar { virtual int calc(int x) = 0; };
    Bar* barX;
    Bar* barY;
    int x,y;
    Foo(Bar* bx,Bar* by) : barX(by),barY(by) {}
    void process(int xx,int yy){
        x = barX->calc(xx);
        y = barY->calc(yy);
    }
    ~Foo();
};

struct IDBar : Foo::Bar { int calc(int x) { return x; } };

int main() {
    IDBar* b = new IDBar();
    Foo f = Foo(b,b);
    Foo f2 = Foo(new IDBar(),new IDBar());
}

我不能使用C ++ 11(即没有智能指针),而且我不是100%肯定... 这是删除Bar的两个(或可能只有一个)的正确方法对象:

Foo::~Foo(){
    if (barX == barY){ delete barX; }
    else { delete barX; delete barY; }
}

PS:我的想法是Foo拥有Bar个对象(因此将负责删除它们)。传递给构造函数的Bar不应该用于其他任何事情。实际上Bar只能属于一个Foo(我后来才意识到这个缺陷,但现在还可以)。此外,Foo不应被复制(也许我应该明确地防止这种情况)。

4 个答案:

答案 0 :(得分:1)

不不不不。上帝没有

Foo::~Foo(){
        if (barX == barY){ delete barX; }
        else { delete barX; delete barY; }
    }

删除对象未自行分配的内存非常危险。无论拥有哪些对象都应删除它们。在你的情况下,主要应该负责删除那些分配的对象。

int main() {
    IDBar* b = new IDBar();
    IDBar* b2 = new IDBar();
    IDBar* b3 = new IDBar();
    Foo f = Foo(b,b);
    Foo f2 = Foo(b2,b3);
    delete b;
    delete b2;
    delete b3;
}

对象处理内存管理本身的一个完美示例是LinkedList。 LinkedList在其自身内部创建节点。用户不知道它们,而只是插入T.然后LinkedList将在删除时删除它们。用户只负责删除LinkedList,因为他或她创建了它。

std::list<MyObject*> list;
MyObject* object = new MyObject;
list.push_back(object);
//The list isn't going to delete object because it doesn't own it
delete object;

答案 1 :(得分:1)

如果你坚持你的建筑,不能听从Taztingo的设计规则;然后答案是肯定的:这是正确的方法!

然后制作类Foo及其构造函数的强大API文档,接管对象Bar的生命责任。

答案 2 :(得分:1)

我认为比较远非完美 - 只是想象你有三个指针。

为了确保永不发生双delete,您只需拨打一个delete怎么样?考虑一下情景:

  1. 分配一个内存池(让我们说应该分配一千个对象)。这应该通过常规new调用在堆上分配,在新的PoolAllocator<T>类中的某个位置,以及在其析构函数中的delete
  2. 实现池,使其支持函数void* getMemory<T>()返回要使用的T内存,并推进内部指针。
  3. 确保调用T的任何内容只能通过池提供的内存中的新位置创建。
  4. 最后,您不必担心内存 - 池对象可以是常规堆栈变量,堆上有内部。

    否则,是什么禁止你实现自己的原型智能指针?

答案 3 :(得分:1)

我对Taztingo略有不同意见。对象获取传入其中的资源的所有权并非不合理。这种情况发生在各种现实世界的代码中。该类获取传递给构造函数的资源的所有权的事实被记录下来,就是这样。如果有人在没有查阅文件的情况下不正当地使用该课程,那么他们就会在脚下拍摄。

然而,这确实是容易出错的,我们希望捕获这些错误或阻止它们发生。如果有某种方法可以保证在Foo时没有其他人认为他们拥有该资源,那么我们就会在路上。在C ++ 11中,使用std::unique_ptr很容易。在早期的C ++标准中,有std::auto_ptr。虽然早期的C ++标准没有移动构造函数,std::auto_ptr的功能类似于在分配或复制到新std::auto_ptr时从旧的std::auto_ptr放弃资源。

我建议您使用std::auto_ptr

如果明确允许为类Foo的两个构造函数参数传递相同资源的情况,请创建一个只接受一个std::auto_ptr<Bar>的新构造函数。

最后,如果您想要使Foo无法复制,只需将复制构造函数和赋值运算符都声明为私有。