简体   繁体   English

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

[英]C++ Pointer data members: Who should delete them?

So say I have a class like this: 所以说我有这样一个类:

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

If someone uses the constructor like this: 如果有人使用这样的构造函数:

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

Who's responsibility is it to call delete on the object newed in the constructor? 谁负责在构造函数中新建的对象上调用delete? In this case, the scope in which the A constructor was called can only delete foo since the SomeHugeClass is anonymous. 在这种情况下,调用A构造函数的范围只能删除foo,因为SomeHugeClass是匿名的。

However, what if someone uses the constructor like this? 但是,如果有人像这样使用构造函数呢?

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

Then, the caller can call delete hugeObj at some point, right? 然后,调用者可以在某个时候调用delete hugeObj,对吗?

Does this implementation of A leak memory on destruction? A的这种实现是否会破坏内存?

I'm working on a project with a lot of object composition done this way and as much as I would love to use smart pointers, I have to talk to the project leads about changing old code to take advantage of that before I can. 我正在研究一个以这种方式完成大量对象组合的项目,尽管我希望使用智能指针,但我必须与项目负责人讨论如何更改旧代码以便在我可以之前利用它。

I try to follow this simple rule whenever it is possible: The one who calls new should call delete as well. 我尽可能地尝试遵循这个简单的规则:调用new应该调用delete Otherwise the code soon becomes too messy to keep track of what is deleted and what is not. 否则,代码很快就会变得太乱,无法跟踪删除的内容和不删除的内容。

In your case, if A::A receives the pointer, it must not delete it. 在你的情况下,如果A :: A收到指针,它不能删除它。 Think of this simple case: 想想这个简单的案例:

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

Class A can not know who else is using that pointer! A类不知道还有谁在使用该指针!

If you want class A to take care of the pointer, it should create it itself. 如果你想要A类来处理指针,它应该自己创建它。

Of course, you could handle both cases, but that might be an overkill, something like this: 当然,你可以处理这两种情况,但这可能是一种矫枉过正,如下所示:

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

In your example caller should be responsible for deleting huge_object because constructor could throw exception and destructor of A will not be called. 在您的示例中,调用者应该负责删除huge_object因为构造函数可能抛出异常,并且不会调用A析构函数。 And your implementation has a memory leak since nobody calls delete now. 而且你的实现有内存泄漏,因为现在没有人调用delete

You could use shared_ptr as follows: 您可以使用shared_ptr ,如下所示:

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

In this case you shouldn't care about deleting SomeHugeClass . 在这种情况下,您不应该关心删除SomeHugeClass

In order for this line below 为了这一行在下面

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

not to result in memory leak, you can make sure to free memory pointed to by m_huge_object in the destructor of class A. 不要导致内存泄漏,你可以确保在A类的析构函数中释放m_huge_object指向的内存。

The class A definition can look something like below: A类定义可能如下所示:

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

What I don't like about the above is that I prefer whoever allocates memory should also be the one responsible to free that memory. 我不喜欢上面的内容是我更喜欢分配内存的人也应该是负责释放内存的人。 Also, there's the problem that Kirill pointed out. 此外,还有基里尔指出的问题。 So keep your definition of class A, and the code can be as simple as: 所以保持你对A类的定义,代码可以简单如下:

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

This is more a design problem. 这更像是一个设计问题。 Traditionnaly, when you pass non-const pointers to a constructor, the object should release the pointer. 传统上,当您将非常量指针传递给构造函数时,该对象应该释放指针。 Pass a const reference if you intend to keep a copy. 如果您打算保留副本,请传递const引用。

An alternate design is something along these lines: SomeHugeClass typically is not that huge (eg. a pointer), but owns a huge amount of memory: 另一种设计是这样的:SomeHugeClass通常不是那么大(例如指针),但拥有大量的内存:

class A
{
    SomeHugeClass m_;

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

This design is possible if SomeHugeClass implements an efficient swap (swapping the pointers). 如果SomeHugeClass实现了有效的交换(交换指针),那么这种设计是可行的。 The constructor makes a copy of x before swapping it into m_ , and if you pass a temporary object to the constructor, the copy may be (and usually will be) elided by the compiler. 构造x在将其交换为m_之前生成x的副本,如果将临时对象传递给构造函数,则编译器可能会(并且通常将)删除该副本。

Note that SomeHugeClass can be replaced here by smart_pointer<SomeHugeClass> giving the semantics you want when you pass smart_pointer(new SomeHugeClass()) to the constructor, since it is temporary. 注意,someHugeClass可以在这里替换为smart_pointer<SomeHugeClass> ,当你将smart_pointer(new SomeHugeClass())传递给构造函数时,它会给你所需的语义,因为它是临时的。

EDIT (for clarity...): Final code may look like 编辑(为清晰起见......):最终代码可能看起来像

class A
{
    smart_ptr<SHC> m_;

public:
    A(smart_ptr<SHC> x) { m_.swap(x); }
};

which has the required behavior when you call A(new SHC(...)) (no copy, and deletion when A gets destructed), and when you call A(smart_ptr<SHC>(a)) (copy of a is performed, and released when A gets destructed). 当你调用A(new SHC(...))没有所需的行为(没有复制,当A被破坏时删除),当你调用A(smart_ptr<SHC>(a)) (执行a副本A(smart_ptr<SHC>(a)) ,当A被破坏时释放)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM