C ++指针数据成员:谁应该删除它们?

时间:2010-07-16 12:28:39

标签: c++

所以说我有这样的课程:

class A {
    public:
        A( SomeHugeClass* huge_object)
            : m_huge_object(huge_object) {}
    private:
        SomeHugeClass* m_huge_object;        
};

如果有人使用这样的构造函数:

A* foo = new A(new SomeHugeClass());

谁负责在构造函数中新建的对象上调用delete?在这种情况下,调用A构造函数的范围只能删除foo,因为SomeHugeClass是匿名的。

但是,如果有人像这样使用构造函数呢?

SomeHugeClass* hugeObj = new SomeHugeClass();
A* foo = new A(hugeObj);

然后,调用者可以在某个时候调用delete hugeObj,对吗?

A的这种实现是否会在销毁时泄漏内存?

我正在研究一个以这种方式完成大量对象组合的项目,尽管我希望使用智能指针,但我必须与项目负责人讨论更改旧代码以利用之前的优势可以。

4 个答案:

答案 0 :(得分:7)

我会尽可能地遵循这个简单的规则:致电new的人也应该致电delete。否则,代码很快就会变得太乱,无法跟踪删除的内容和不删除的内容。

在你的情况下,如果A :: A收到指针,它就不能删除它。想想这个简单的案例:

SomeHugeClass* hugeObj = new SomeHugeClass();
A * a1 = new A(hugeObj);
A * a2 = new A(hugeObj);

A类不能知道还有谁正在使用该指针!

如果你想要A类来处理指针,它应该自己创建它。

当然,你可以处理这两种情况,但这可能是一种矫枉过正,如下所示:

A::A() : own_huge_object(true) {
    m_huge_object = new SomeHugeClass();
}
A::A(SomeHugeClass * huge_object ) : own_huge_object(false) {
    m_huge_object = huge_object;
}
A::~A() { if(own_huge_object) delete m_huge_object; }

答案 1 :(得分:3)

在您的示例中,调用者应负责删除huge_object,因为构造函数可能抛出异常,并且不会调用A的析构函数。而且你的实现有内存泄漏,因为现在没有人调用delete

您可以按如下方式使用shared_ptr

class A {
    public:
        A( shared_ptr<SomeHugeClass> huge_object)
            : m_huge_object(huge_object) {}
    private:
        shared_ptr<SomeHugeClass> m_huge_object;        
};

在这种情况下,您不应该关心删除SomeHugeClass

答案 2 :(得分:0)

为了下面的这一行

A* foo = new A(new SomeHugeClass());

导致内存泄漏,您可以确保释放A类析构函数中 m_huge_object 指向的内存。

A类定义如下所示:

class A {
  public:
    A(SomeHugeClass* huge_object)
        : m_huge_object(huge_object) {}
    ~A() { delete m_huge_object; }
  private:
    SomeHugeClass* m_huge_object;        
};

我不喜欢上面的内容是我更喜欢分配内存的人也应该是负责释放内存的人。此外,还有基里尔指出的问题。所以保持你对A类的定义,代码可以简单如下:

SomeHugeClass* hugeObj = new SomeHugeClass();
A* foo = new A(hugeObj); 
//some code
delete hugeObj;
delete foo;

答案 3 :(得分:0)

这更像是一个设计问题。传统上,当您将非常量指针传递给构造函数时,该对象应该释放指针。如果您打算保留副本,请传递const引用。

另一种设计是这样的:SomeHugeClass通常不是那么大(例如指针),但拥有大量的内存:

class A
{
    SomeHugeClass m_;

public:
    A(SomeHugeClass x) { m_.swap(x); }
};

如果SomeHugeClass实现了有效的交换(交换指针),那么这种设计是可行的。构造函数在将x交换为m_之前复制smart_pointer<SomeHugeClass>,如果将临时对象传递给构造函数,则编译器可能会(并且通常将)删除该副本。

注意SomeHugeClass可以在这里被class A { smart_ptr<SHC> m_; public: A(smart_ptr<SHC> x) { m_.swap(x); } }; 替换,在你将smart_pointer(new SomeHugeClass())传递给构造函数时给出你想要的语义,因为它是临时的。

编辑(为清晰起见......): 最终代码可能看起来像

A(new SHC(...))

,当你调用A(smart_ptr<SHC>(a))时没有所需的行为(没有副本,当A被破坏时删除),以及当你调用a时,{{1}}的副本被执行,并被释放当A被破坏时。)