如果类没有构造函数,编译器会为它创建一个默认构造函数吗?
C ++新手的程序员经常有两个常见的误解:
为每个类合成默认构造函数 没有定义一个
来自C ++对象模型内容
我不知所措......
答案 0 :(得分:14)
在引用此引用的部分中对此进行了详细说明。我不会完整地解释它,但这里是部分内容的简短摘要。
首先,您需要了解以下术语:implicitly-declared
,implicitly-defined
,trivial
,non-trivial
和synthesized
(使用的术语)作者:Stanley Lippman,但未在标准中使用。)
的隐式声明强> 的
如果此类中没有implicitly-declared
构造函数,则构造函数为user-declared
。例如,此类struct T { };
未声明任何构造函数,因此编译器隐式声明默认构造函数。另一方面,这个类struct T { T(int); };
声明了一个构造函数,因此编译器不会声明一个隐式的默认构造函数。除非您定义了自己的默认构造函数,否则您将无法创建没有参数的T
实例。
的隐式定义强> 的
implicitly-declared
构造函数在使用时为implicitly-defined
,即在没有参数的情况下创建实例时。假设以下课程struct T { };
,行T t;
将触发T::T()
的定义。否则,您将遇到链接器错误,因为构造函数将被声明但未定义。 但是,隐式定义的构造函数不一定有任何与之关联的代码!默认构造函数是 合成 (意味着创建了一些代码)仅在某些情况下由编译器编写。
琐碎的构造函数
implicitly-declared
默认构造函数为trivial
时:
trivial
个构造函数和trivial
个构造函数。在这种情况下,默认编译器无关,因此没有合成代码。例如,在以下代码中
struct Trivial
{
int i;
char * pc;
};
int main()
{
Trivial t;
}
t
的构造不涉及任何操作(您可以通过查看生成的程序集来看到:没有调用构造函数来构造t
)。
的非平凡强> 的
另一方面,如果该类不满足上述三个要求,则其implicitly-declared
默认构造函数将为non-trivial
,这意味着它将涉及必须执行的一些操作以便尊重语言语义。在这种情况下,编译器将synthesize
执行这些操作的构造函数的实现。
例如,请考虑以下类:
struct NonTrivial
{
virtual void foo();
};
由于它具有虚拟成员函数,因此其默认构造函数必须将虚拟表指针设置为正确的值(假设实现使用虚拟方法表)。
同样,这个类的构造函数
struct NonTrivial
{
std::string s;
};
必须调用字符串默认构造函数,因为它不是trivial
。要执行这些操作,编译器会为默认构造函数生成代码,并在您创建不带参数的实例时随时调用它。您可以通过查看与此实例化NonTrivial n;
对应的程序集来检查这一点(您应该看到函数调用,除非构造函数已内联)。
<强>摘要强>
如果没有为类提供任何构造函数,编译器会隐式声明一个默认构造函数。如果您尝试使用它,编译器会隐式定义它,如果可以的话(例如,当一个类具有非默认构造成员时,它并不总是可行的)。但是,这个隐式定义并不意味着生成任何代码。编译器需要为构造函数生成代码(合成它),只要它是非平凡的,这意味着它涉及实现语言语义所需的某些操作。
<强> N.B。强>
Stanley B Lippman的“Inside the C ++ object model”,这个答案涉及(可能的)C ++实现,而不是它的语义。因此,上述所有内容都不能推广到所有编译器:据我所知,即使对于一个简单的构造函数,也完全允许实现生成代码。从C ++用户的角度来看,重要的是“隐式声明/定义”方面(以及琐碎/非平凡的区别,因为它具有一些含义(例如,具有非平凡的类的对象)构造函数不能是union的成员))。
答案 1 :(得分:11)
我认为误解是:
为每个没有定义
的类合成默认构造函数
如果您不自己声明,那么人们会认为默认的构造函数(不接受任何参数)将始终生成。
然而,事实并非如此,因为如果您自己声明任何构造函数,则不会自动创建默认构造函数。
class MyClass {
public:
MyClass(int x) {}; // No default constructor will be generated now
};
这会导致像初学者期望使用MyClass
这样的问题:
MyClass mc;
哪个不起作用,因为没有默认构造函数不接受args。
编辑,因为OP仍然有点困惑。
想象一下,我上面的MyClass
就是这样:
class MyClass {
};
int main() {
MyClass m;
}
那会编译,因为编译器会自动生成默认构造函数MyClass()
,因为使用了MyClass
。
现在看一下:
#include <iostream>
class MyClass {
};
int main() {
std::cout << "exiting\n";
}
如果这是唯一的代码,编译器甚至不会生成默认构造函数,因为永远不会使用MyClass
。
现在这个:
#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)
作为默认构造函数,因为它不接受任何参数,但它不是由编译器生成的。
最后,初学者可能遇到问题:
class MyClass() {
public:
MyClass(int x) { _x = x; }
int _x;
};
int main() {
MyClass m;
}
上面的内容会在编译过程中给你一个错误,因为MyClass m
需要一个默认的构造函数(没有参数),但是你已经声明了一个带int
的构造函数。在这种情况下,编译器也不会生成无参构造函数。
答案 2 :(得分:4)
如果出现以下情况,则为每个未定义一个类的类合成默认构造函数:
答案 3 :(得分:3)
到目前为止,所有赞成的答案似乎都大致相同:
为每个没有任何用户定义的构造函数的类合成默认构造函数。
这是对问题中声明的修改,这意味着
为每个没有用户定义的默认构造函数的类合成默认构造函数。
差异很重要,但陈述仍然是错误的。
正确的陈述是:
为每个没有任何用户定义的构造函数的类合成默认构造函数,并且所有子对象在类的上下文中都是默认构造的。
以下是第一个陈述的一些明确的反例:
struct NoDefaultConstructor
{
NoDefaultConstructor(int);
};
class Surprise1
{
NoDefaultConstructor m;
} s1; // fails, no default constructor exists for Surprise1
class Surprise1
没有用户定义的构造函数,但没有合成默认构造函数。
子对象是成员还是基础无关紧要:
class Surprise2 : public NoDefaultConstructor
{
} s2; // fails, no default constructor exists for Surprise2
即使所有子对象都是可默认构造的,也必须可以从复合类中访问默认构造函数:
class NonPublicConstructor
{
protected:
NonPublicConstructor();
};
class Surprise3
{
NonPublicConstructor m;
} s3; // fails, no default constructor exists for Surprise3
答案 4 :(得分:0)
是的,如果您没有定义自己的构造函数,默认构造函数始终存在(请参阅默认构造函数部分here)。
答案 5 :(得分:0)
http://www.codeguru.com/forum/archive/index.php/t-257648.html
引用:
Stanley B. Lippman撰写的“Inside the C ++ object model”一书中提到了以下内容。
编译器的类有四个特征 需要为声明为no的类合成默认构造函数 构造函数。标准将这些视为隐含的, 非平凡的默认构造函数。合成的构造函数完成 只有实施需要。它通过调用成员对象或 基类默认构造函数或初始化虚函数 或每个对象的虚拟基类机制。没有的类 展示这些特征并且根本不宣布构造函数 据说有隐含的,普通的默认构造函数。在实践中, 这些简单的默认构造函数不是合成的。 ... C ++新手的程序员经常有两个常见的误解:
为每个类合成默认构造函数 没有定义一个
- 中声明的每个数据成员的默认初始值设定项
编译器合成的默认构造函数提供显式 在类
如您所见,这些都不是真的。