01 September 2022

C++ 核心指南目录

C.33: If a class has an owning pointer member, define a destructor

理由

在对象析构的时候,必须删除其所拥有的对象。

例子

指针成员代表着一个资源。不建议这样使用T*,但是在早期代码里,这种用法很常见。考虑到T*可能占有资源,以下代码就可能有问题了。

template<typename T>
class Smart_ptr {
    T* p;   // BAD: vague about ownership of *p
    // ...
public:
    // ... no user-defined default operations ...
};

void use(Smart_ptr<int> p1)
{
    // error: p2.p leaked (if not nullptr and not owned by some other code)
    auto p2 = p1;
}

注意,如果你定义了析构函数,那么你要么定义所有的默认操作,要么删除所有的默认操作:

template<typename T>
class Smart_ptr2 {
    T* p;   // BAD: vague about ownership of *p
    // ...
public:
    // ... no user-defined copy operations ...
    ~Smart_ptr2() { delete p; }  // p is an owner!
};

void use(Smart_ptr2<int> p1)
{
    auto p2 = p1;   // error: double deletion
}

默认的复制操作只会浅浅地将p1.p复制到p2.p,从而导致重复释放p1.p。注意以下代码中的所有权关系:

template<typename T>
class Smart_ptr3 {
    owner<T*> p;   // OK: explicit about ownership of *p
    // ...
public:
    // ...
    // ... copy and move operations ...
    ~Smart_ptr3() { delete p; }
};

void use(Smart_ptr3<int> p1)
{
    auto p2 = p1;   // OK: no double deletion
}

注意

通常来说,获得一个析构函数的最简单的方法是用智能指针( std::unique_ptr )替代原始指针,然后让编译器去隐式的分配合适的析构方法。

注意

为何不要求所有的指针都必须是“智能指针”?因为强求的话,有时候会导致大量的代码变更,而且可能影响 ABI。

强化

  • 类中如果有指针成员数据,就要仔细检查下是否有问题
  • 类中如果有owner<T*>就必须定义其默认操作函数