封装对象的生命周期管理

发布于 2024-07-11 20:24:47 字数 1079 浏览 9 评论 0原文

封装对象并管理其生命周期的最佳方法是什么? 示例:我有一个类 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 的行为,我可以忽略这些问题吗? 声明责任并让其他人阅读文档就足够了吗? 在您的代码库中如何管理它?

What is the best approach to encapsulate objects and manage their lifetime? Example: I have a class A, that contains an object of type B and is solely responsible for it.

Solution 1, clone b object to ensure that only A is able to clean it up.

class A
{
    B *b;
public:
    A(B &b)
    {
        this->b = b.clone();
    }

    ~A()
    {
        delete b; // safe
    }
};

Solution 2, directly use the passed object, we risk a potential double free here.

class A
{
    B *b;
public:
    A(B *b)
    {
        this->b = b;
    }

    ~A()
    {
        delete b; // unsafe
    }
};

In my actual case, solution #2 would fit best. However I wonder if this is considered bad code because someone might not know about the behavior of A, even if it's documented. I can think of these scenarios:

B *myB = new B();
A *myA = new A(myB);
delete myB; // myA contains a wild pointer now

Or,

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

Can I just ignore these issues if I properly document the behavior of A? Is it enough to declare the responsibility and leave it up to the others to read the docs? How is this managed in your codebase?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

鲜肉鲜肉永远不皱 2024-07-18 20:24:47

除非确实必要,否则我绝不会自己删除任何内容。 这会导致错误。

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

因此,要么将解决方案 1 与 auto_ptr 结合使用,要么将解决方案 2 与 shared_ptr 结合使用。

I never delete anything myself unless I really have to. That leads to errors.

Smart pointers are your friend. std::auto_ptr<> is your friend when one object owns another and is responsible for deleting it when going out of scope. boost::shared_ptr<> (or, now, std::tr1::shared_ptr<>) is your friend when there's potentially more than one object attached to another object, and you want the object deleted when there's no more references to it.

So, either use your solution 1 with auto_ptr, or your solution 2 with shared_ptr.

扶醉桌前 2024-07-18 20:24:47

您应该定义对象,以便所有权语义尽可能由接口定义。 正如 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 执行某些操作, 他们用来构造一个A,构造完成后,操作将失败,因为它们持有的指针将无效。

You should define your object so that the ownership semantics are, as much as possible, defined by the interface. As David Thornley pointed out, std::auto_ptr is the smart pointer of choice to indicate transfer of ownership. Define your class like so:

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

Since the contract of std::auto_ptr is that assignment = transfer of ownership, your constructor now states implicitly that an A object has ownership of the pointer to B it's passed. In fact, if a client tries to do something with a std::auto_ptr<B> that they used to construct an A after the construction, the operation will fail, as the pointer they hold will be invalid.

携余温的黄昏 2024-07-18 20:24:47

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

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

有关该主题的更多信息:引用计数

If you are writing code that someone else will be using later, these issues must be addressed. In this case I would go for simple reference counting (maybe with smart pointers). Consider the following example:

When an instance of the encapsulating class is assigned an object B, it calls a method to increase object's B reference counter. When the encapsulating class is destroyed, it doesn't delete B, but instead calls a method do decrease reference count. When the counter reaches zero, object B is destroyed (or destroys itself for that matter). This way multiple instances of encapsulating class can work with a single instance of object B.

More on the subject: Reference Counting.

笑红尘 2024-07-18 20:24:47

如果您的对象仅对传递的对象负责,那么删除它应该是安全的。 如果不安全,那么“您独自承担责任”的说法就是错误的。 那么是哪一个呢? 如果您的接口记录表明您将删除入站对象,那么调用者有责任确保您收到必须由您删除的对象。

If your object is solely responsible for the passed object then deleting it should be safe. If it is not safe than the assertion that you are solely responsible is false. So which is it? If you're interface is documented that you WILL delete the inbound object, then it is the caller responsibility to make sure you receive an object that must be deleted by you.

醉态萌生 2024-07-18 20:24:47

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

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

作为参考,当我想到“克隆”这个术语时,它意味着一个深层复制,它也会克隆 B。 我预计克隆后两个 A 会完全分离。

If you're cloning A, and both A1 and A2 retain references to B, then B's lifetime is not being controlled entirely by A. It's being shared among the various A. Cloning B ensures a one-to-one relationship between As and Bs, which will be easy to ensure lifetime consistency.

If cloning B is not an option, then you need to discard the concept that A is responsible for B's lifetime. Either another object will need to manage the various B, or you'll need to implement a method like reference counting.

For reference, when I think of the term 'Clone', it implies a deep copy, which would clone B as well. I'd expect the two As to be completely detached from each other after a clone.

客…行舟 2024-07-18 20:24:47

我不会克隆不必要的东西或“只是为了安全”。

相反,我知道删除某些内容是谁的责任:要么通过文档,要么通过智能指针……例如,如果我有一个创建某些内容并返回指向它的指针的函数,但它不会不要删除它,这样就不清楚该东西应该在哪里以及由谁删除,那么我可以定义 create',而不是 create 返回裸指针返回类型作为返回某种智能指针中包含的指针。

I don't clone things unnecessarily or "just to be safe".

Instead I know whose responsibility it is to delete something: either via documentation, or by smart pointers ... for example, if I have a create function which instantiates something and returns a pointer to it and doesn't delete it, so that it's unclear where and by whome that thing is ever supposed to be deleted, then instead of create's returning a naked pointer I might define create's return type as returning the pointer contained within some kind of smart pointer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文