编译器为类创建的所有成员函数是什么?这会一直发生吗?

时间:2010-09-17 09:48:05

标签: c++ member-functions c++-faq

编译器为类创建的所有成员函数是什么?这会一直发生吗?像析构函数。 我关心的是它是否为所有类创建,为什么需要默认构造函数?

5 个答案:

答案 0 :(得分:82)

C ++ 98/03

如果需要,

  1. 编译器将为您生成默认构造函数,除非您声明自己的任何构造函数。
  2. 编译器将为您生成 副本 构造函数,除非您声明自己的。
  3. 编译器将为您生成 副本 赋值运算符,除非您声明自己的。
  4. 编译器将为您生成析构函数,除非您声明自己的。
  5. 正如Péter在一篇有用的评论中所说,所有这些仅在需要时由编译器生成。 (不同之处在于,当编译器无法创建它们时,只要不使用它们就可以。)


    C ++ 11

    C ++ 11添加了以下规则,对于C ++ 14 也是如此(对于towi,请参阅this comment

    • 如果编译器生成 移动 构造函数
      • 没有用户声明的 副本 构造函数
      • 没有用户声明的 副本 分配运算符
      • 没有用户声明的 移动 分配运营商
      • 没有用户声明的析构函数
      • 标记为已删除
      • 并且所有成员和基地都可移动
    • 类似于 移动 赋值运算符:如果没有用户定义则会生成
      • 没有用户声明的 副本 构造函数
      • 没有用户声明的 副本 分配运算符
      • 没有用户声明的 移动 构造函数
      • 没有用户声明的析构函数
      • 标记为已删除
      • 并且所有成员和基地都可移动

    请注意,这些规则比C ++ 03规则更精细,在实践中更有意义。

    为了更方便地了解上述内容Thing

    的内容
    class Thing {
    public:
        Thing();                        // default constructor
        Thing(const Thing&);            // copy c'tor
        Thing& operator=(const Thing&); // copy-assign
        ~Thing();                       // d'tor
        // C++11:
        Thing(Thing&&);                 // move c'tor
        Thing& operator=(Thing&&);      // move-assign
    };
    

    进一步阅读,如果你是C ++ - 初学者考虑一个不要求你实现过去五年中任何一个的设计,又名 The Rule Of Zero Martinho Fernandes

答案 1 :(得分:2)

你的意思是'创造'的'定义'吗?

$ 12.1 - “默认构造函数(12.1),复制构造函数和复制赋值运算符(12.8)以及析构函数(12.4)是特殊成员函数。

如果'created'表示'已定义',那么这里是C ++标准的重要部分。

- 当用于创建类类型(1.8)的对象时,隐式定义了类的隐式声明的默认构造函数。

- 如果类没有用户声明的析构函数,则会隐式声明析构函数。当隐式声明的析构函数用于销毁其类类型的对象时,它是隐式定义的。

- 如果类定义没有显式声明复制构造函数,则会隐式声明一个。如果隐式声明的复制构造函数用于从其类类型的对象或从其类类型派生的类类型的副本初始化其类类型的对象,则会隐式定义它。

- 如果类定义没有显式声明一个复制赋值运算符,则会隐式声明一个。当为类类型的对象分配其类类型的值或从其类类型派生的类类型的值时,隐式定义隐式声明的复制赋值运算符。

答案 2 :(得分:1)

默认情况下,如果用户未实现,编译器会向该类添加一些成员函数。那些被称为四巨头:

  • 默认构造函数
  • 复制构造函数
  • 复制操作员(作业)
  • 析构者

根据成员的类型和您自己列出的成员函数,将不会生成这些成员。

答案 3 :(得分:0)

其他答案已告诉您创建的内容,并且编译器可能仅在使用时生成它们。

  

我关心的是它是否是为所有类创建的......

为何关注?认为它在可执行文件中创建了不需要的代码?不太可能,但您可以轻松地检查您的环境。

或许你担心的是它可能不会在你想要的时候创建一个构造函数?没有什么可担心的......如果需要,它们总是被创建,而不是由用户提供。

  

...为什么需要默认构造函数?

因为类中可能包含自己的析构函数,需要系统地调用它们。例如,给定......

struct X
{
    std::string a;
    std::string b;
};

...默认的析构函数确保a和b的析构函数运行。

答案 4 :(得分:0)

C ++ 17 N4659标准草案

https://github.com/cplusplus/draft/blob/master/papers/n4659.pdf 6.1“声明和定义”中有一条注释,可能总结了所有这些内容:

  

3   [注意:在某些情况下,C ++实现会隐式定义默认构造函数(15.1),复制   构造函数(15.8),移动构造函数(15.8),副本赋值运算符(15.8),移动赋值运算符(15.8),   或析构函数(15.4)成员函数。 —尾注] [示例:给定

#include <string>

struct C {
  std::string s;         // std::string is the standard library class (Clause 24)
};

int main() {
  C a;
  C b = a;
  b = a;
}
     

该实现将隐式定义函数以使C的定义等效于

struct C {
  std::string s;
  C() : s() { }
  C(const C& x): s(x.s) { }
  C(C&& x): s(static_cast<std::string&&>(x.s)) { }
  // : s(std::move(x.s)) { }
  C& operator=(const C& x) { s = x.s; return *this; }
  C& operator=(C&& x) { s = static_cast<std::string&&>(x.s); return *this; }
  // { s = std::move(x.s); return *this; }
  ~ C() { }
};
     

—结束示例]

声明这些条件的条件在Conditions for automatic generation of default/copy/move ctor and copy/move assignment operator?

中进行了说明。

确保某事物具有默认设置的一种很酷的方法是尝试使它使用= default,如What does "default" mean after a class' function declaration?

所述

下面的示例可以做到这一点,并且还可以行使所有隐式定义的功能。

#include <cassert>
#include <string>

struct Default {
    int i;
    Default()                          = default;
    Default(const Default&)            = default;
    Default& operator=(Default&)       = default;
    Default& operator=(const Default&) = default;
    Default(Default&&)                 = default;
    Default& operator=(Default&&)      = default;
    ~Default()                         = default;
};

struct Instrument {
    int i;
    static std::string last_call;
    Instrument()                             { last_call = "ctor"; }
    Instrument(const Instrument&)            { last_call = "copy ctor"; }
    Instrument& operator=(Instrument&)       { last_call = "copy assign"; return *this; }
    Instrument& operator=(const Instrument&) { last_call = "copy assign const"; return *this; }
    Instrument(Instrument&&)                 { last_call = "move ctor";  }
    Instrument& operator=(Instrument&&)      { last_call = "move assign"; return *this; }
    ~Instrument()                            { last_call = "dtor"; }
};
std::string Instrument::last_call;

int main() {
    // See what the default constructors are doing.
    {
        // Default constructor.
        Default ctor;
        // i is uninitialized.
        // std::cout << ctor.i << std::endl;
        ctor.i = 1;

        // Copy constructor.
        Default copy_ctor(ctor);
        assert(copy_ctor.i = 1);

        // Copy assignment.
        Default copy_assign;
        copy_assign = ctor;
        assert(copy_assign.i = 1);

        // Copy assignment const.
        const Default const_ctor(ctor);
        Default copy_assign_const;
        copy_assign_const = const_ctor;
        assert(copy_assign_const.i == 1);

        // Move constructor.
        Default move_ctor(std::move(ctor));
        assert(move_ctor.i == 1);

        // Move assignment.
        Default move_assign;
        move_assign = std::move(ctor);
        assert(move_assign.i == 1);
    }

    // Check that the constructors are called by these calls.
    {
        // Default constructor.
        Instrument ctor;
        assert(Instrument::last_call == "ctor");

        // Copy constructor.
        Instrument copy_ctor(ctor);
        assert(Instrument::last_call == "copy ctor");

        // Copy assignment.
        copy_ctor = ctor;
        assert(Instrument::last_call == "copy assign");

        // Copy assignment const.
        const Instrument const_ctor(ctor);
        Instrument copy_assign_const;
        copy_assign_const = const_ctor;
        assert(Instrument::last_call == "copy assign const");

        // Move constructor.
        Instrument move_ctor(std::move(ctor));
        assert(Instrument::last_call == "move ctor");

        // Move assignment.
        Instrument move_assign;
        move_assign = std::move(ctor);
        assert(Instrument::last_call == "move assign");

        // Destructor.
        {
            Instrument dtor;
        }
        assert(Instrument::last_call == "dtor");
    }
}

GitHub upstream

在GCC 7.3.0中进行了测试:

g++ -std=c++11 implicitly_defined.cpp