摘要:本文首先会解释清楚构造函数的作用,然后通过提问和举例的方式说明在什么情况下编译器会为我们自动生成构造函数。
关于构造函数的认知误区
误区:构造函数的作用和它的名字一样,用于构造对象的。❌
正解:构造函数仅用来处理对象初始化,而非用于构造对象,真正的对象在函数申明或执行new的时候就以已经分配好内存。✔️
误区:如果没有手动添加默认构造函数,编译器会自动为我们添加默认构造函数。❌
正解:编译器只会在需要的时候才会自动创建默认构造函数。✔️
分析: 首先介绍一个工具用来查看是否编译器生成了构造函数,在window下可以使用dumpbin的工具用来分析编译后的obj文件,然后就可以在分析后的信息中查找相应的构造函数,如果没有就表示编译器没有生成构造函数。
dumpbin /ALL test.obj >> test.txt
#include
using namespace std;
class MyTestA{};
int main(int argc)
{
MyTestA a;
cout<<"hello";
return 0;
}
对于上面这个例子,按以前的理解类MyTestA编译器应该会自动生成构造函数,但是经过分析obj文件后编译器并没有自动生成构造函数。
你可能会有疑问了,那么什么情况下编译器才会自动生成构造函数呢?
第一种情况:
//test01#include
using namespace std;
class MyTestB{
public:
MyTestB(){};
};
class MyTestA{
public:
MyTestB b;
};
int main(int argc)
{
MyTestA a;
cout<<"hello";
return 0;
}
分析test01.obj文件可以看到如下字样,就说明编译器已经给自动生成了默认构造函数。因为在MyTestA中的成员变量类型MyTestB有默认构造函数,在使用对象a之前需要初始化其成员b,那么什么时候调用MyTestB的构造函数来初始化b呢?所以就需要为MyTestA生成默认构造函数来且插入MyTestB的构造函数代码来初始化b。
结论01:当该类(MyTestA)包含一个类类型的成员(MyTestB)且该成员有一个构造函数,那么编译器会为该类(MyTestA)生成一个默认构造函数且在该构造函数中自动插入类类型的成员(MyTestB)的构造函数。
第二种情况:
//test02class MyTestB{
public:
MyTestB(){};
};
class MyTestA:public MyTestB{};
int main(int argc)
{
MyTestA a;
cout<<"hello";
return 0;
}
通过分析test02.obj发现同样编译器也自动生成了默认构造函数。
结论02:当类(MyTestA)继承自含默认构造函数的父类(MyTestB)时,编译器也会为类(MyTestA)自动生成默认构造函数且往该构造函数中插入父类构造函数代码。
第三种情况:
//test03#include
using namespace std;
class MyTestA{
public:
virtual void DisPlay();
};
int main(int argc)
{
MyTestA a;
cout<<"hello";
return 0;
}
通过分析test03.obj发现同样编译器也自动生成了默认构造函数。因为该类存在虚函数,那么势必编译器会自动给我们生成虚函数表和成员变量虚函数指针,对于虚函数指针不是我们手动就能够初始化的,必须在使用对象前对虚函数指针进行初始化,所以编译器必须生成一个默认构造函数来初始化虚函数指针。对于虚函数指针等知识可以参见另一篇文章《面试|C++的多态,虚函数,虚基类...你搞清楚了吗?》。
结论03:当该类(MyTestA)中包含有虚函数的时候,编译器会为其自动生成默认构造函数。
第四种情况:
//test04#include
using namespace std;
class MyTestD{};
class MyTestC:virtual public MyTestD{};
class MyTestB:virtual public MyTestD{};
class MyTestA:public MyTestC,public MyTestB{};
int main(int argc)
{
MyTestA a;
cout<<"hello";
return 0;
}
通过分析test04.obj发现同样编译器也自动生成了默认构造函数。可以看到MytestA既没有虚函数且其父类也有没有默认构造函chengcheng数,但结果编译器同样也自动生成了默认构造函数,为什么?仔细看发现MytestA类是有一个虚基类MyTestD,既然有了虚基类那么势必编译器就会生成一个虚基类指针用来指向虚基类表(基类成员的偏移量,防止数据冗余与二义性),所以为了在使用对象前能够初始化所有成员,就必须要有一个构造函数来初始化虚基类指针。
结论04:如果一个类带有虚基类,编译器也会为它合成一个默认构造函数。
总结
编译器只有在需要的时候才会自动生成默认构造函数。
当类包含一个含默认构造函数的类类型的成员时,那么编译器会自动生成一个默认构造函数。
当类继承自含默认构造函数的父类时,那么编译器会自动生成一个默认构造函数。
当该类中包含有虚函数的时候,那么编译器会自动生成一个默认构造函数。
当该类带有虚基类的时候,那么编译器会自动生成一个默认构造函数。
end
往期推荐
面试|C++的多态,虚函数,虚基类...你搞清楚了吗?
轻松搞定hashtable
面试|搬了这么久的砖,居然还不知道什么叫“踩内存”