是真的默认构造函数是合成的每个类,不定义一个? [英] Is it true that a default constructor is synthesized for every class that does not define one?

查看:162
本文介绍了是真的默认构造函数是合成的每个类,不定义一个?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果类没有构造函数,编译器会为它创建一个默认构造函数吗?


新的C ++程序员通常有两种常见的误解:



构造函数是为每个类合成
没有定义一个


从C ++对象模型内部



我在损失...

解决方案

该报价的摘要部分。



首先,您需要了解以下术语: implicitly-declared 隐式定义 trivial 非平凡合成(由Stanley Lippman使用,但未在标准中使用的术语)。



隐含声明



构造函数如果在这个类中没有用户声明的构造函数,则隐式声明。例如, struct T {}; 不声明任何构造函数,因此编译器隐式声明一个默认构造函数。另一方面,这个类 struct T {T(int); }; 声明一个构造函数,因此编译器不会声明一个隐式默认构造函数。除非您定义自己的默认构造函数,否则您将无法创建不带参数的 T 实例。



隐含定义



隐式声明隐式定义,当它被使用时,即当一个实例创建没有参数。假设以下类 struct T {}; T t; 将触发 T :: T()。否则,您将有一个链接器错误,因为构造函数将被声明但未定义。 但是,隐式定义的构造函数不一定具有任何与其关联的代码!默认构造函数 合成







当:

$ b $ p $ 默认构造函数 b

  • 其类没有虚函数和虚基类,

  • 其基类有 trivial 构造函数和

  • 所有非静态成员都有 trivial 构造函数。



在这种情况下,默认编译器没有任何作用,因此没有合成代码。例如,在下面的代码中

  struct Trivial 
{
int i;
char * pc;
};

int main()
{
Trivial t;
}

t 不涉及任何操作(您可以看到通过查看生成的程序集:没有调用构造函数来构造 t )。



不重要



另一方面,如果类不符合三要求,其隐式声明的默认构造函数将是非平凡,意味着它将涉及一些操作必须执行以便尊重语言语义。在这种情况下,编译器将合成执行这些操作的构造函数的实现。



例如,下面的类:

  struct NonTrivial 
{
virtual void foo
};

由于它有一个虚拟成员函数,它的默认构造函数必须虚拟表指针到正确的值(假设实现使用虚拟方法表,当然)。



同样,这个类的构造函数

  struct NonTrivial 
{
std :: string s;
};

必须调用字符串默认构造函数,因为 trivial 。为了执行这些操作,编译器生成默认构造函数的代码,并在创建没有参数的实例时调用它。你可以通过查看对应于这个实例化的程序集来检查这一点: NonTrivial n; (你应该看到一个函数调用,除非构造函数被内联)。






摘要



你的类的任何构造函数,编译器隐式声明一个默认的。如果你试图使用它,编译器隐式地定义它,如果它可以(它不总是可能的,例如当一个类有一个非默认可构造的成员)。然而,这个隐含的定义并不意味着生成任何代码。编译器需要生成构造函数的代码(合成它),只有当它是非平凡的,意味着它涉及实现语言语义所需的某些操作。






NB



Stanley B Lippman的C ++对象模型内部可能)实现C ++,而不是它的语义。因此,上面没有一个可以推广到所有编译器:据我所知,一个实现完全允许生成代码,即使对于一个琐碎的构造函数。从C ++用户的角度来看,所有重要的是隐式声明/定义方面(以及琐碎/不平凡的区别,因为它有一些含义(例如,一个类的对象具有非平凡构造函数不能是联合的成员))。


If the class doesn't have the constructor, will the compiler make one default constructor for it ?

Programmers new to C++ often have two common misunderstandings:

That a default constructor is synthesized for every class that does not define one

from the book Inside the C++ Object Model

I am at a loss...

解决方案

This is well explained in the section from which this quote is taken. I will not paraphrase it in its entirety, but here is a short summary of the section content.

First of all, you need to understand the following terms: implicitly-declared, implicitly-defined, trivial, non-trivial and synthesized (a term that is used by Stanley Lippman, but is not used in the standard).

implicitly-declared

A constructor is implicitly-declared for a class if there is no user-declared constructor in this class. For example, this class struct T { }; does not declare any constructor, so the compiler implicitly declares a default constructor. On the other hand, this class struct T { T(int); }; declares a constructor, so the compiler will not declare an implicit default constructor. You will not be able to create an instance of T without parameters, unless you define your own default constructor.

implicitly-defined

An implicitly-declared constructor is implicitly-defined when it is used, i.e. when an instance is created without parameters. Assuming the following class struct T { };, the line T t; will trigger the definition of T::T(). Otherwise, you would have a linker error since the constructor would be declared but not defined. However, an implicitly-defined constructor does not necessarily have any code associated with it! A default constructor is synthesized (meaning that some code is created for it) by the compiler only under certain circumstances.

trivial constructor

An implicitly-declared default constructor is trivial when:

  • its class has no virtual functions and no virtual base classes and
  • its base classes have trivial constructors and
  • all its non-static members have trivial constructors.

In this case, the default compiler has nothing to do, so there is no code synthesized for it. For instance, in the following code

struct Trivial
{
    int i;
    char * pc;
};

int main()
{
    Trivial t;
}

the construction of t does not involve any operations (you can see that by looking at the generated assembly: no constructor is called to construct t).

non-trivial

On the other hand, if the class does not meet the three requirements stated above, its implicitly-declared default constructor will be non-trivial, meaning that it will involve some operations that must be performed in order to respect the language semantics. In this case, the compiler will synthesize an implementation of the constructor performing these operations.

For instance, consider the following class:

struct NonTrivial
{
    virtual void foo();
};

Since it has a virtual member function, its default constructor must set the virtual table pointer to the correct value (assuming the implementation use a virtual method table, of course).

Similarly, the constructor of this class

struct NonTrivial
{
    std::string s;
};

must call the string default constructor, as it is not trivial. To perform these operations, the compiler generates the code for the default constructor, and calls it anytime you create an instance without parameters. You can check this by looking at the assembly corresponding to this instantiation NonTrivial n; (you should see a function call, unless the constructor has been inlined).


Summary

When you don't provide any constructor for your class, the compiler implicitly declares a default one. If you try to use it, the compiler implicitly defines it, if it can (it is not always possible, for instance when a class has a non-default-constructible member). However, this implicit definition does not imply the generation of any code. The compiler needs to generate code for the constructor (synthesize it) only if it is non-trivial, meaning that it involves certain operations needed to implement the language semantics.


N.B.

Stanley B Lippman's "Inside the C++ object model" and this answer deals with (a possible) implementation of C++, not its semantics. As a consequence, none of the above can be generalized to all compilers: as far as I know, an implementation is perfectly allowed to generate code even for a trivial constructor. From the C++ user point of view, all that matters is the "implicitly-declared/defined` aspect (and also the trivial/non-trivial distinction, as it has some implications (for instance, an object of a class with non-trivial constructor cannot be a member of a union)).

这篇关于是真的默认构造函数是合成的每个类,不定义一个?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆