2009-01-12 26 views
2

封装对象并管理其生命周期的最佳方法是什么?例如:我有一个类A,其中包含类型为B的对象,并且是单独负责的封装对象的生命周期管理

解决方案1 ​​,克隆b对象,以确保只有一个能够清理。

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的行为?宣布责任并让其他人阅读文档就足够了吗?这是如何在你的代码库中管理的?

回答

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

由于性病:: auto_ptr的合同是所有权的分配对象=转移,现在你的构造规定隐含指针的一个对象具有所有权到B它的通过。事实上,如果客户端尝试使用来执行std :: auto_ptr <B>,他们在构造之后用它来构造A,那么操作将失败,因为它们所持有的指针将无效。

2

如果您正在编写其他人将在以后使用的代码,则必须解决这些问题。在这种情况下,我会去进行简单的引用计数(也许用智能指针)。考虑下面的例子:

当一个封装类的实例被分配了一个对象B时,它调用一个方法来增加对象的B引用计数器。当封装类被销毁时,它不会删除B,而是调用一个方法来减少引用计数。当计数器达到零时,对象B被销毁(或为此而销毁)。这样封装类的多个实例可以使用对象B的单个实例。

更多关于这个问题:Reference Counting

+0

所以,你会改变A的构造函数采取类似shared_ptr包含b? – driAn 2009-01-12 20:24:47

+0

不需要引用计数。如A所述,拥有所有权。 – 2009-01-12 20:37:39

1

如果您克隆了A,并且A1和A2都保留对B的引用,那么B的生存期并不完全由A来控制。它在各个A之间共享。克隆B确保一对一关系As和Bs之间,这将很容易确保生命周期的一致性。

如果克隆B不是一个选项,那么您需要放弃A负责B的生命周期的概念。另一个对象需要管理各种B,否则您需要实现一个像引用计数的方法。

作为参考,当我想到术语'克隆'时,它意味着一个深层副本,它也会克隆B.我希望两个As在克隆之后完全脱离彼此。

0

我不会不必要地克隆东西或“为了安全起见”。

相反,我知道删除某些东西的责任:通过文档或智能指针......例如,如果我有一个create函数,它实例化某个东西并返回一个指向它的指针并且不删除它,所以它不清楚在哪里以及哪个东西被删除了,然后代替create返回a裸指针我可能定义create的返回类型为返回包含在某种智能指针内的指针。

2

如果您的对象完全负责传递的对象,那么删除它应该是安全的。如果这不是安全的,那么你断言你完全负责是错误的。那它是哪一个?如果您的界面记录为您将删除入站对象,那么确保您收到一个必须由您删除的对象是主叫方的责任。

5

我从来没有删除任何东西,除非我真的必须。这导致错误。

智能指针是你的朋友。 std::auto_ptr<>是你的朋友,当一个对象拥有另一个对象并负责在超出范围时删除它。当有可能有多个对象附加到另一个对象时,并且希望在没有更多对象引用它时删除该对象,则可以使用boost::shared_ptr<>(或现在std::tr1::shared_ptr<>)。

因此,请使用您的解决方案1与auto_ptr,或者您的解决方案2与shared_ptr