1.无参构造-默认构造函数
无参并非严格的没有参数的构造函数,而是不需要提供实际参数的构造函数,比如存在有默认参数
Integer::Integer(int a = 10);//也算是默认构造函数,可以无参调用。
Integer* p1 = new Integer();
Integer* p2 = new Integer;
Integer i();
Integer i;//建议方式
关于对象的无参构造在使用时可以不用()的不带();
第三种构造方式下,如果该Integer不存在无参构造函数,会直接将其当做函数声明从而可能引发编译器报错。
2.单参构造-类型转换构造
单参构造函数,向编译器提供一种隐式的类型转换。
explicit Integer::Integer(int data);
- 比如函数返回值为 Integer 类型,而实际返回的为int类型,则会隐式调用类型转换构造函数将int类型构造为Integer类型。
- 比如形参为Integer类型,而实际调用时使用的是 int类型,编译器同样会隐式调用类型转换构造函数将int类型构造为Integer类型。
- 如果不让编译器隐式类型转换,则可以在该类型转换构造函数前添加explicit修饰,如此,如果发生隐式类型转换则编译器报错。如果想要转换则必须使用static_cast进行显示转换。
3.拷贝构造
拷贝构造函数必须传递引用型参数。
Integer::Integer(Integer const& that);
- 如果未定义构造函数,则系统提供缺省构造函数和缺省拷贝构造;
- 如果定义了非拷贝构造,则系统也提供缺省拷贝构造;
- 如果定义了拷贝构造,则系统不再提供构造函数。
即如果一个类如果只有一个构造函数,则一定是拷贝构造函数。
另外: 如果传递非引用参数,则会通过拷贝构造形成一个匿名的Integer副本,导致无限调用自身,陷入死循环。
初始化表:
初始化表不是执行过程,而是指导成员变量的定义和初始化,初始化过程是按照类中成员变量的声明顺序,而非初始化表中的顺讯。
初始化表在以下情况下必要:
类的类类型成员变量,要么在初始化表中显式的初始化,要么通过相应类型的缺省构造函数初始化。
但是,如果类成员没有缺省构造函数,则必须使用初始化表来初始化。
class A {
public:
A (int data) : m_data (data) {}
int m_data;
};
class B {
public:
B (int data) : m_a (data) {}
A m_a; //B无法缺省构造,因为A无法缺省构造。
};
另外类的常量型和引用型成员变量,必须在初始化表中显示初始化,不能在构造函数体中初始化。
本质上,初始化表中的声明属于类成员的定义并初始化,而构造函数体中属于赋值。
故而:常量型和引用型成员因为必须在定义时就初始化,所以必须在初始化表中进行显式初始化。
构造函数的执行顺序:
用A类定义一个对象或指向A对象的指针,首先是分配sizeof(A)的内存,然后调用A的构造函数,进行初始化A的对象。
也是new包含malloc和调用A:A(...)构造函数。
父子类的构造析构顺序:
构造时先调用基类的构造函数,再调用派生类的构造函数
析构时先调用派生类的析构函数,再调用基类的析构函数。