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

编译器为一个类创建的所有成员函数是什么? 这是否一直发生? 像破坏者一样。我担心的是是否为所有类创建了它,为什么需要默认构造函数?

Onnesh asked 2019-11-04T18:08:52Z
5个解决方案
85 votes

C ++ 98/03

如果需要的话

  1. 除非您声明自己的任何构造函数,否则编译器将为您生成一个默认构造函数。
  2. 除非您声明自己的副本,否则编译器将为您生成一个副本构造函数。
  3. 除非您声明自己的副本,否则编译器将为您生成一个副本分配运算符。
  4. 除非您声明自己的,否则编译器会为您生成一个析构函数。

正如Péter在有用的评论中所说,所有这些仅在需要时由编译器生成。 (不同之处在于,当编译器无法创建它们时,只要不使用它们就可以了。)


C ++ 11

C ++ 11添加了以下规则,C ++ 14也是如此(向towi鸣谢,请参阅此评论):

  • 如果发生以下情况,编译器将生成move构造函数:
    • 没有用户声明的副本构造函数,并且
    • 没有用户声明的副本分配运算符,并且
    • 没有用户声明的移动分配运算符,并且
    • 没有用户声明的析构函数,
    • 它没有标记为deleted,
    • 并且所有成员和基地都是可移动的。
  • 同样,对于移动分配运算符,如果
    • 没有用户声明的副本构造函数,并且
    • 没有用户声明的副本分配运算符,并且
    • 没有用户声明的move构造函数,并且
    • 没有用户声明的析构函数,
    • 它没有标记为deleted,
    • 并且所有成员和基地都是可移动的。

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

为了更容易理解以上内容:

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 ++初学者,请考虑一种设计,该设计不需要您执行Martinho Fernandes撰写的文章中的五个零规则(又称零规则)。

sbi answered 2019-11-04T18:11:21Z
2 votes

您是用“创造”来定义“定义”吗?

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

如果“创建”的意思是“定义的”,那么这是C ++标准的重要部分。

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

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

-如果类定义未明确声明副本构造函数,则隐式声明一个副本构造函数。 如果隐式声明的副本构造函数用于从其类类型的对象或从其类类型派生的类类型的对象的副本中初始化其类类型的对象,则将隐式定义该构造函数。

-如果类定义未显式声明一个拷贝赋值运算符,则隐式声明一个。 当为其类类型的对象分配其类类型的值或从其类类型派生的类类型的值时,将隐式定义一个隐式声明的副本分配运算符。

Chubsdad answered 2019-11-04T18:12:24Z
1 votes

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

  • 默认构造函数
  • 复制构造函数
  • 复制运算符(分配)
  • 析构函数

根据成员的类型以及您提供的列出的成员函数,将不会全部生成这些成员函数。

Klaim answered 2019-11-04T18:13:14Z
1 votes

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)成员函数。 —尾注] [示例:给定

= default

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

= default

- 结束例子]

声明这些条件的条件在以下位置进行说明:自动生成默认值/复制/移动控制器和复制/移动分配运算符的条件?

确保某事物具有默认值的一种很酷的方法是尝试使它使用= default,如以下解释:在类的函数声明之后,“默认值”是什么意思?

下面的示例做到了这一点,并且还练习了所有隐式定义的函数。

#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上游。

在GCC 7.3.0中进行了测试:

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

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

我担心的是是否为所有课程创建了...

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

也许您担心的是,当您想要一个构造函数时可能不会创建一个构造函数? 不用担心...总是在需要时创建它们,而不是由用户提供。

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

因为类内部可能有带有自己的析构函数的对象,所以需要系统地调用它们。 例如,给定...

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

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

Tony Delroy answered 2019-11-04T18:15:37Z
translate from https://stackoverflow.com:/questions/3734247/what-are-all-the-member-functions-created-by-compiler-for-a-class-does-that-hap