封装对象和管理其生命周期的最佳方法是什么?示例:我有一个 A 类,其中包含 B 类型的对象,并且对此负责。
解决方案1 ,克隆 b 对象以确保只有 A 能够清理它。
class A
{
B *b;
public:
A(B &b)
{
this->b = b.clone();
}
~A()
{
delete b; // safe
}
};
解决方案2 ,直接使用传递的对象,我们冒险潜在的双重免费。
class A
{
B *b;
public:
A(B *b)
{
this->b = b;
}
~A()
{
delete b; // unsafe
}
};
在我的实际案例中,解决方案#2最适合。但是我想知道这是否被认为是错误的代码,因为有人可能不知道 A 的行为,即使它已被记录。我可以想到这些场景:
B *myB = new B();
A *myA = new A(myB);
delete myB; // myA contains a wild pointer now
或者,
B *myB = new B();
A *firstA = new A(myB);
A *secondA = new A(myB); // bug! double assignment
delete firstA; // deletes myB, secondA contains a wild pointer now
delete secondA; // deletes myB again, double free
如果我正确记录A的行为,我可以忽略这些问题吗?是否足以宣布责任并让其他人阅读文档?如何在您的代码库中管理它?
答案 0 :(得分:5)
聪明的指针是你的朋友。当一个对象拥有另一个对象时,std::auto_ptr<>
是您的朋友,并且当超出范围时负责将其删除。 boost::shared_ptr<>
(或者,现在,std::tr1::shared_ptr<>
)是您的朋友,当可能有多个对象附加到另一个对象时,您希望在没有更多引用的情况下删除该对象。
因此,要么将解决方案1与auto_ptr
一起使用,要么将解决方案2与shared_ptr
一起使用。
答案 1 :(得分:4)
您应该定义您的对象,以便尽可能由接口定义所有权语义。正如David Thornley指出的那样,std :: auto_ptr是指示所有权转移的智能指针。像这样定义你的类:
class A
{
std::auto_ptr<B> b;
public:
A(std::auto_ptr<B> b)
{
this->b = b;
}
// Don't need to define this for this scenario
//~A()
//{
// delete b; // safe
//}
};
由于std :: auto_ptr的契约是赋值=所有权转移,你的构造函数现在隐式地声明A对象拥有指向它传递的B的指针。实际上,如果客户端尝试使用std :: auto_ptr&lt; B&gt;执行某些操作。他们在构造之后用来构造A,操作将失败,因为他们持有的指针将无效。
答案 2 :(得分:2)
如果您正在编写其他人稍后将使用的代码,则必须解决这些问题。在这种情况下,我会去简单的引用计数(可能有智能指针)。请考虑以下示例:
当为封装类的实例分配了一个对象B时,它会调用一个方法来增加对象的B引用计数器。当封装类被销毁时,它不会删除B,而是调用方法确实减少引用计数。当计数器达到零时,对象B被破坏(或者为此而破坏自身)。这样,封装类的多个实例可以与对象B的单个实例一起使用。
有关此主题的更多信息:Reference Counting。
答案 3 :(得分:2)
如果您的对象全权负责传递的对象,则删除它应该是安全的。如果不完全断言你自己负责是假的。那是哪个呢?如果您的接口被记录为您将删除入站对象,则调用者有责任确保您收到必须由您删除的对象。
答案 4 :(得分:1)
如果您正在克隆A,并且A1和A2都保留对B的引用,则B的生命周期不完全由A控制。它在各种A之间共享。克隆B确保了一对一之间的关系As和Bs,这将很容易确保终身的一致性。
如果克隆B不是一个选项,那么你需要抛弃A负责B的生命周期的概念。另一个对象需要管理各种B,否则你需要实现像引用计数这样的方法。
作为参考,当我想到“克隆”一词时,它意味着一个深层拷贝,它也会克隆B。我希望在克隆之后两个As完全脱离。
答案 5 :(得分:0)
我不会不必要地克隆东西或“只是为了安全”。
相反,我知道删除某些内容的责任是:通过文档或智能指针...例如,如果我有一个create
函数实例化某些内容并返回一个指向它的指针并且没有删除它,以便不清楚那个东西应该被删除的地方和那个,然后代替create
返回一个裸指针我可能会定义create
的返回类型作为返回指针包含在某种智能指针中。