为什么我们需要默认生成的析构函数

时间:2020-07-18 22:42:12

标签: c++ c++11 destructor

在什么情况下,我们需要使用默认生成的析构函数?很明显,为什么我们需要默认生成的构造函数和operator =,但无法想到应该使用默认生成的析构函数的情况。

new webpack.DefinePlugin({
  __DEVELOPMENT__: env.NODE_ENV === 'development'
});

3 个答案:

答案 0 :(得分:2)

如果您想将某个类的实现隐藏在一个内部类中,并在该内部类的实例(pimpl惯用语)中保留unique_ptr,则需要移动由于default无法使用不完整的类型,因此unique_ptr的析构函数定义超出了类定义。

示例:

A.hpp (该类的用户将包括的标题)

#pragma once
#include <memory>

class A {
public:
    A();
    ~A();
    void foo() const;
private:
    struct A_impl; // just forward declared
    std::unique_ptr<A_impl> pimpl;
};

A_impl.hpp (“隐藏”-不包含在A的常规用法中)

#pragma once
#include "A.hpp"

struct A::A_impl {
    void foo() const;
};

A.cpp

#include "A_impl.hpp"

A::A() : pimpl(std::make_unique<A_impl>()) {}
A::~A() = default;                            // <- moved to after A_impl is fully defined
void A::foo() const { pimpl->foo(); }

A_impl.cpp

#include "A_impl.hpp"

#include <iostream>

void A::A_impl::foo() const { std::cout << "foo\n"; }

Demo

如果让编译器生成A::~A(),它将不会编译。我的编译器说:

unique_ptr.h:79:16: error: invalid application of ‘sizeof’ to incomplete type ‘A::A_impl’
     static_assert(sizeof(_Tp)>0,
                   ^~~~~~~~~~~

Demo

答案 1 :(得分:0)

C++ Core Guidelines C.21: If you define or =delete any copy, move, or destructor function, define or =delete them all

原因

复制,移动和销毁的语义紧密相关,因此,如果需要声明一个,那么其他人也需要考虑。

声明任何copy / move / destructor函数,即使是= default或= delete,也将取消对move构造函数和move赋值运算符的隐式声明。声明移动构造函数或移动赋值运算符(甚至= default或= delete)也将导致隐式生成的副本构造函数或隐式生成的副本赋值运算符被定义为已删除。因此,一旦声明了其中的任何一个,就应该声明所有其他的,以避免产生不必要的影响,例如将所有潜在的举动变成更昂贵的副本,或者仅对类进行举动。

注意 如果要使用默认实现(在定义另一个实现时),请编写= default以表明您正在为此功能故意这样做。如果您不希望生成默认函数,请使用= delete禁止它。

所以这主要取决于类中声明的内容。

通常大约是The rule of three/five/zero

如果该类需要自定义的复制/移动功能,但对于析构函数没有特殊要求,则应在析构函数上使用=default

答案 2 :(得分:0)

这似乎是在询问何时为类定义析构函数,该析构函数的主体是否与编译器生成的主体相同。

原因包括:

  1. 清晰度。如果您的类具有复制/移动构造函数或复制/移动赋值运算符,则它通常在管理某些资源。许多编码准则都要求您定义析构函数,以表明它不仅被忽略,甚至等同于编译器生成的析构函数。
  2. 该函数的某些方面与编译器生成的方面不同。如果需要虚拟析构函数,则必须对其进行定义。同样,必须定义一个抛出析构函数。
  3. 您要控制析构函数的生成位置。您可以在类定义之外定义析构函数。您可能需要对循环依赖的类执行此操作,就像在其他答案之一中一样。您可能需要执行此操作以定义稳定的ABI。您可能需要执行此操作以控制代码生成。

在所有这些情况下,即使主体没什么特别的,您也必须或想要定义析构函数。为什么要使用= default而不是空的正文?因为编译器生成的析构函数与= default所获得的析构函数等效,并且您只想更改要尝试进行更改的析构函数的各个方面。空主体与C ++中的= default不同,因为可以将默认函数定义为Deleted。空的身体也排除了微不足道的可破坏性,即使那是可以选择的。