指针类成员和删除职责

时间:2011-07-22 02:13:37

标签: c++ pointers memory-management

如果我的类有一个可以由类客户端设置的指针,我应该如何处理删除?

示例:

class A {
};

class B {
public:
   void setA(A* a) {
       this->a = a;
   }
private:
   A* a;
};

如何成为班级B的析构函数?它应该删除a吗?正如我所看到的,用户可以通过两种方式设置此指针:

... // Assume B object b being created elsewhere
A aObj;
A* aPtr = new A();

b.setA(&aObj); // It is not OK to use delete, and class member will
               // point to invalid memory location once aObj goes out
               // of scope
b.setA(aPtr);  // Using new will make the pointer available even after
               // this block of code
...

那么删除b的正确方法是什么?我应该始终在new方法中执行set吗?

6 个答案:

答案 0 :(得分:7)

  

B类的析构函数应该如何?它应该删除吗?

,该类的作者,决定它的语义。不要考虑指针,引用和delete。从设计的角度思考:AB之间的关系是什么? B需要A为什么?

两种常见的关系类型是委托和组合。委托意味着客户端代码使用setA成员使实例知道可能用于进一步使用的其他B实例。组合意味着setA成员用于初始化实例的内部部分。

委派的一种可能实现是使用成员指针。我建议传递一个setA的引用来分配给那个指针;它回避了检查0的问题,并使客户端代码明白没有所有权问题需要处理。这与多态类型兼容。

组合的一种可能实现是使用A成员,并通过引用传递给const或按值分配给它。另一种方法是使用智能指针,特别是如果A是多态使用的话。通过智能指针传递是最简单的事情 - 但你必须检查0和/或记录演员。

无论您决定使用什么(无论如何都不必在此列表中),请使用代码作为工具来实现您的目的或设计。不要让代码决定你的想法。

在我的所有示例实现中,您不必在析构函数中执行任何特殊操作。

答案 1 :(得分:2)

你真的应该真的没有这样的课程。相反,使用资源管理容器(如shared_ptrunique_ptr)来保存指向动态分配对象的指针。

正如您可以轻松看到的那样,如果您在整个地方随机分配动态对象,您将无法跟踪谁应该负责的事情。怎么样复制和分配你的课程?那么构造函数中的异常呢?不要这样做。

答案 2 :(得分:1)

我认为通常会有3种情况,请参阅下面的代码:

//class B doesn't own a
class B {
public:
   void setA(A& a) {
       m_a = a;
   }
private:
   A& m_a;              //Only a reference , so need to worry about delete
};


//class B owns A
class B {
public:
    void setA(std::auto_ptr<A>& a) {
       m_a.reset(a.release());
   }
private:
    boost::scoped_ptr<A> m_a;   //m_a got deleted when instance of B lifetime end
};


//class B shared A with someone else 
class B {
public:
    void setA(boost::shared_ptr<A>& a) {
       m_a = a;
   }
private:
    boost::shared_ptr<A> m_a;   //m_a got deleted when no one need this pointer anymore(reference counting reduced to 0)
};

答案 3 :(得分:0)

从外观上看,您正在尝试在B类中创建对A对象的引用。

要正确清理B,您必须先检查A是否为空。有点像...

~B()
{
    if (A)
    {
        delete A;
        A = 0;
    }
}

请记住,这也删除了类之外的A对象,因为它们引用了相同的内存。因此,在这种情况下,如果删除了A对象,则非常容易引用指向无效内存的指针。

我不会将它与btw的局部变量一起使用,因为当它超出范围时你将丢失它的地址。但是,另一方面是本地指针..那么,一旦离开创建A的范围,就不必担心引用无效内存。

答案 4 :(得分:0)

你有一个设计决定。谁应该拥有这个对象?

  • B对象拥有A对象,setA将所有权传递给B.B的析构函数应删除A.
  • 某些外部代码拥有A对象。 B不会删除它,但会依赖于外部代码来在适当的时候销毁它。
  • 智能指针跟踪对A对象的引用,并在销毁所有引用时自动删除它。

智能指针的第三个选项是最简单和最可靠的,但所有3个选项都可以使用。诀窍是挑选一个并慎重考虑它。

答案 5 :(得分:0)

在设计分析期间,您必须回答以下问题:

  1. 对象A是否依赖于对象B的生命周期,如果是,则使用“组合”,在这种情况下,对象B将创建A并负责在对象B本身被销毁之前删除它。
  2. 如果对象A独立于对象B的生命周期,则使用“聚合”。您可以通过B的构造函数或set方法向B提供对象A.对象B不必担心破坏对象A,但你必须确定在B的生命周期中A处于有效状态。
  3. 如果A依赖于对象B的生命周期,但您需要在B之前创建A,那么执行“依赖注入”。它就像聚合一样,你可以在构造函数或set方法中将A传递给B,但是在这种情况下A被B专用,没有其他对象使用A. B在自己销毁之前删除A.