首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >是否为每个未定义类合成了默认构造函数?

是否为每个未定义类合成了默认构造函数?
EN

Stack Overflow用户
提问于 2012-03-09 14:34:01
回答 5查看 5.1K关注 0票数 6

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

新加入C++的程序员通常有两个共同的误解: 为每个未定义类合成默认构造函数。

来自C++对象模型内部的书

我不知所措..。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2012-03-09 15:57:31

这一点在摘取这一引文的一节中得到了很好的解释。我将不完整地解释它,但这里是一节内容的简短摘要。

首先,您需要理解以下术语:implicitly-declaredimplicitly-definedtrivialnon-trivialsynthesized (斯坦利普曼使用的术语,但在标准中没有使用)。

implicitly-declared

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

implicitly-defined

implicitly-declared构造函数在使用时是implicitly-defined,即在没有参数的情况下创建实例时。假设下面的类是struct T { };,行T t;将触发T::T()的定义。否则,您将有一个链接器错误,因为构造函数将被声明,但没有定义。但是,隐式定义的构造函数不一定有任何与其相关联的代码!默认构造函数是合成的(意味着某些代码是在特定情况下为它创建的)。

平凡构造函数

在以下情况下,implicitly-declared默认构造函数为trivial

  • 它的类没有虚拟函数,也没有虚拟基类,
  • 它的基类有trivial构造函数和
  • 它的所有非静态成员都有trivial构造函数。

在这种情况下,默认编译器没有什么可做的,因此没有为其合成代码。例如,在以下代码中

代码语言:javascript
运行
复制
struct Trivial
{
    int i;
    char * pc;
};

int main()
{
    Trivial t;
}

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

非平凡

另一方面,如果类不满足上述三项要求,那么它的implicitly-declared默认构造函数将是non-trivial,这意味着它将涉及一些必须执行的操作,以便尊重语言语义。在这种情况下,编译器将synthesize执行这些操作的构造函数的实现。

例如,考虑以下类:

代码语言:javascript
运行
复制
struct NonTrivial
{
    virtual void foo();
};

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

类似地,该类的构造函数

代码语言:javascript
运行
复制
struct NonTrivial
{
    std::string s;
};

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

摘要

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

N.B.

斯坦利普曼的“内部C++对象模型”和这个答案涉及(一种可能的)实现的C++,而不是它的语义。因此,上述任何一个都不能推广到所有编译器:据我所知,即使是为一个普通的构造函数,实现也完全允许生成代码。从C++用户的角度来看,重要的是“隐式声明/定义”方面(以及琐碎/非平凡的区分,因为它有一些含义(例如,具有非平凡构造函数的类的对象不能是联合的成员)。

票数 14
EN

Stack Overflow用户

发布于 2012-03-09 14:39:39

我认为误解是:

为每个未定义类合成默认构造函数。

人们认为不接受参数的默认构造函数将始终生成,如果您不自己声明它。

但是,这是不正确的,因为如果您自己声明任何构造函数,则不会自动创建默认构造函数。

代码语言:javascript
运行
复制
class MyClass {
public:
    MyClass(int x) {}; // No default constructor will be generated now
};

这将导致一些问题,比如初学者期望使用这样的MyClass

代码语言:javascript
运行
复制
MyClass mc;

这将无法工作,因为没有接受任何args的默认构造函数。

编辑作为OP仍然有点困惑。

假设我上面的MyClass是这样的:

代码语言:javascript
运行
复制
class MyClass {
};

int main() {
    MyClass m;
}

这将编译,因为编译器将自动生成默认构造函数MyClass(),因为使用了MyClass

现在看一看这个:

代码语言:javascript
运行
复制
#include <iostream>

class MyClass {

};

int main() {
    std::cout << "exiting\n";
}

如果这是唯一的代码,编译器甚至不会去生成默认的构造函数,因为从来没有使用过MyClass

现在这是:

代码语言:javascript
运行
复制
#include <iostream>

class MyClass {
public:
    MyClass(int x = 5) { _x = x; }
    int _x;
};

int main() {
    MyClass m;
    std::cout << m._x;
}

编译器不会生成默认的构造函数MyClass(),因为类已经有了由我定义的构造函数。这将有效,MyClass(int x = 5)作为默认构造函数工作,因为它可以不接受任何参数,但它不是由编译器生成的。

最后,初学者可能会遇到问题:

代码语言:javascript
运行
复制
class MyClass() {
public:
    MyClass(int x) { _x = x; }
    int _x;
};

int main() {
    MyClass m;
}

上面的代码将在编译期间抛出一个错误,因为MyClass m需要一个默认的构造函数(没有参数)才能工作,但是您已经声明了一个接受int的构造函数。在这种情况下,编译器也不会生成无参数构造函数。

票数 11
EN

Stack Overflow用户

发布于 2012-03-09 14:36:09

如果以下情况下,将为未定义一个类的每个类合成默认构造函数:

  • 使用类的代码需要一个&只有在
  • 您没有为类显式定义其他构造函数。
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9635772

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档