有关在何处删除继承层次结构中的资源的最佳实践

Its*_*sik 8 c++ oop

请考虑以下代码:

class Base {
protected: 
    int* ptr_;

public:
    virtual ~Base() { /* delete ptr_ ?? */ }    
}

class A : public Base {
public:
    A() { ptr_ = new int; }
    ~A() { /* delete ptr_ ?? */ }
}
Run Code Online (Sandbox Code Playgroud)

我和我的同事发生了轻微的争执.
哪个析构函数应该删除ptr_
我认为它应该在A,因为它是分配的地方.
他认为它应该存在Base,因为那是指针所在的位置.

请告诉我为什么一种方法比另一种方法更好(如果有一种方法,这不仅仅是品味问题)

编辑
很多人质疑设计.在实践中,代码要复杂得多,并且模拟了涉及几个相机的图像处理系统.Base的作用是管理系统的资源(存在一些配置).类A和其他派生类型决定Base了系统的配置并将系统初始化为特定配置.我想现在你可以问一下,如果继承是合成的正确选择.Ais-a Base,只是一种特定的类型,这就是我们选择继承的原因.在这种情况下,ptr_是一个指向摄像机特定驱动程序的指针,它将由派生类型决定,这就是它在那里分配的原因.

Ale*_* C. 7

每个班级都应该管理自己的资源.当你有多个派生类时,每个必须记住释放指针.这很糟糕,因为它复制了代码.

如果允许某些派生类不为指针指定新值,则在基类构造函数中将其设置为零.如果应该允许某些派生类将指针分配给自动变量的地址,那么请检查您的设计.

使用智能指针使问题完全消失(当我grep delete *在我的整个代码库中,我发现没有匹配:)


Dav*_*eas 6

问题是设计不止一个实现.我倾向于同意你的同事,如果指针在基础中,它似乎表明基础对象负责管理资源,而不是派生类型.

事实上,奇怪的是派生构造函数正在修改基本成员这一事实,将指针传递给基础构造函数可能更有意义,在这种情况下语义会更清晰:基础接收资源在施工期间,并负责在其生命周期内进行管理.

如果基础不应该管理资源,那么为什么指针处于该级别?为什么它不是派生类型的成员?

请注意,在管理资源时,您应该考虑三者规则 1:如果您必须实现copy-constructor,assignment-operator析构函数之一,那么您可能希望实现其中的三个(另外考虑实现no) - swap这将是方便的).

一旦你将复制构造函数赋值运算符添加到混合中,你可能会看到管理资源的自然位置就在那里.

1该规则是通用的,因为有充分的理由不总是实现所有这些规则.例如,如果您在实现RAII的成员(作为智能指针)内管理您的资源,您可能需要提供copy-constructionassignment-operator以及深度复制语义,但不需要销毁.或者,在某些情况下,您可能希望禁用*copy-construction*和/或赋值.

  • @ltsik:所以你希望基类中的指针避免重复`AnotherBase*ptr;`这是非常无害的(如果任何派生类忘记,编译器会告诉),但你愿意复制`delete ptr ;`在所有派生类型中,这更危险(如果一个子类忘记,那么你泄漏资源)?超越代码思考:您希望指针位于基类(详细信息)中,还是要将资源管理系统考虑到基类?再次,我会在构造函数中传递指针并管理整个生命周期. (2认同)

Mat*_* M. 1

您担心代码重复,但恐怕它影响了您的判断。

我知道 DRY 得到了很多媒体的关注(并且有充分的理由),但你误解了它。不重复代码和不重复功能之间是有区别的。

DRY是指不重复功能。可能存在看起来相似的代码模式,但用于不同的目标,这些代码模式不应合并,因为每个目标都可以独立转移(并且取消合并是一场噩梦)。他们的相似纯属巧合

这里就是这种情况。int* ptr_您可以在Base(不使用它的)类中任意强制使用 a ,因为所有派生类肯定都需要它。你怎么知道 ?目前事实证明所有派生类都需要它,但这是一个巧合。他们可以选择依赖其他东西。

因此,明智的设计是在基类中提供一个接口,并让每个派生类选择自己的实现:

class Base { public: virtual ~Base() {} };

class D1: public Base {
public:
  D1(Foo const& f): ptr_(new Foo(f)) {}

private:
  std::unique_ptr<Foo> ptr_;
};

class D2: public Base {
public:
  D2(Bar const& f): ptr_(new Bar(f)) {}

private:
  std::unique_ptr<Bar> ptr_;
};
Run Code Online (Sandbox Code Playgroud)

另一方面,如果该类Base要提供一些需要数据成员的通用功能,那么当然可以在类实现中提升这些功能Base......尽管可以说这是不成熟的优化,因为再次,一些派生班级可能会选择以不同的方式做事。