1. 什么是默认构造函数
首先默认构造函数的目的是,如果程序员没有显式地为一个类定义某个特殊成员函数而又需要用到该特殊成员函数时,则编译器会隐式的为这个类生成一个默认的特殊成员函数。
我们一般会认为默认构造函数就是编译器自动生成的那个构造函数,其实这种理解不全面。
准确的说,默认构造函数就是在调用时不需要显示地传入实参的构造函数。根据这个原则,下面2种构造函数都是默认构造函数;
class Sample {
public:
// 默认构造函数。
Sample() {
// do something
}
};
class Sample {
public:
// 默认构造函数。虽然有形参,但有默认值,调用的时候可以不显示的传入实参。
Sample(int m = 10) {
// do something
}
};
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==
2.默认构造函数什么时候被调用
如果定义一个对象时没有使用初始化式,编译器就会使用默认构造函数。如:
派生类和基类的关系:
我们通常说的派生类和基类,我们调用派生类的自定义的构造函数的时候,派生类会自动调用基类中的default construct函数,而不能调用基类中的其他构造函数(除非在派生类的构造函数序列初始化的时候指明基类的构造函数,否则将会自动调用基类默认构造函数)
//不能通过,因为bar派生类不能调用基类中的defalut函数,因为不存在
class Foo
{
private:
int val;
Foo(int i):val(i){}
};
class Bar:public Foo
{
public:
char *str;
int i;
Bar(int i,char*s){
i=i;
str=s;
}
};
// 要想通过编译,除非在基类显示增加默认构造函数或者在派生类中构造函数序列初始化的时候显示指明调用基类自定义的构造函数
// 增加默认构造函数
class Foo
{
Foo(){};
private:
int val;
Foo(int i):val(i){}
};
class Bar:public Foo
{
public:
char *str;
int i;
Bar(int i,char*s){
i=i;
str=s;
}
};
// 或者在派生类构造函数显示调用基类自定义构造函数
class Foo
{
private:
int val;
Foo(int i):val(i){}
};
class Bar:public Foo
{
public:
char *str;
int i;
Bar(int i,char*s):Foo(10, "Hello Sven"){
i=i;
str=s;
}
};
3. default默认构造函数的作用
在C++中约定如果一个类中自定义了带参数的构造函数,那么编译器就不会再自动生成默认构造函数,也就是说该类将不能默认创建对象,只能携带参数进行创建一个对象;
但有时候需要创建一个默认的对象但是类中编译器又没有自动生成一个默认构造函数,那么为了让编译器生成这个默认构造函数就需要default这个属性。
先看例子:
class A
{
public:
A() = default;
A(int B) {
b = B;
};
private:
int b;
};
// 定义一个默认的对象
A a;
//如果没有A() = default; 那么就会报错,或者需要你手动实现一个A() { b =0;},所以我认为default只是为实现提供了方便;
至于系统的实现和我们自己的实现是否完全一致,这个不好确定,感觉也没有必要确定。
编译器生成一个合成版本的构造/析构函数(包括拷贝构造,赋值构造,移动构造,移动赋值构造)
关于构造函数:
如果用户没有定义,在需要的时候编译器会生成一个默认的构造函数,这个规则你应当是知道的。
但是,假如用户定义了其他构造函数(比如有参数的,或者参数不同的),那么编译器无论如何就不会再合成默认的构造函数了
那么如果要使用无参数版本,用户必须显示的定义一个无参版本的构造函数。
如果使用default指示的办法,那么可以产出比用户定义的无参构造函数有更优的代码(毕竟是编译器干活)
还有一个作用可以让使用者一眼就看出这是一个合成版本的构造函数(相当于知道类的作者没干其他事情)
总结:
无论是显式的默认构造函数(=default),还是隐式合成的默认构造函数(编译器生成),都是用来控制默认初始化过程的。它按照如下规则初始化类的数据成员:
1.如果存在类内的初始值,用它来初始化成员。
2.如果不存在类内的初始值,默认初始化该成员。