构造函数
构造函数是一种可初始化其类的实例的成员函数。——MSDN
在派生类的构造函数中,基类和成员构造函数的调用顺序如下。 首先,调用基构造函数,然后按照基类成员在类声明中出现的顺序对这些成员进行初始化,所有类类型(class type)的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中;然后,调用派生构造函数。
#include <iostream>
using namespace std;
class Contained1 {
public:
Contained1() {
cout << "Contained1 constructor." << endl;
}
};
class Contained2 {
public:
Contained2() {
cout << "Contained2 constructor." << endl;
}
};
class Contained3 {
public:
Contained3() {
cout << "Contained3 constructor." << endl;
}
};
class BaseContainer {
public:
BaseContainer() {
cout << "BaseContainer constructor." << endl;
}
private:
Contained1 c1;
Contained2 c2;
};
class DerivedContainer : public BaseContainer {
public:
DerivedContainer() : BaseContainer() {
cout << "DerivedContainer constructor." << endl;
}
private:
Contained3 c3;
};
int main() {
DerivedContainer dc;
int x = 3;
}
输出:
Contained1 constructor. //1
Contained2 constructor. //2
BaseContainer constructor. //3
Contained3 constructor. //4
DerivedContainer constructor. //5
对于本例来说,派生类DerivedContainer
首先调用基类BaseContainer
的构造方法,基类的构造方法会根据成员在基类中出现的顺序初始化类型为class
的成员c1
和c2
,所以首先输出的1,2,然后在执行基类构造方法的函数体,输出3。基类完事之后,派生类初始化自己的成员,输出4,接着执行派生类自己的函数体,输出5。
成员列表(Member Lists)
使用成员初始值设定项列表从构造函数参数初始化类成员。 此方法使用直接初始化,这比在构造函数体内使用赋值运算符更高效。
#include <iostream>
struct A {
A() { std::cout << "A::A()\n"; }
A(int) { std::cout << "A::(int)\n"; }
void operator=(const A&) { std::cout << "A::operator=(const A&)\n"; }
};
struct C1 {
A a;
C1(int i) {
a = i;
}
};
struct C2 {
A a;
C2(int i) : a(i) {}
};
int main() {
std::cout << "How expesive is it to create a C1?\n";
{ C1 c1(7); }
std::cout << "How expensive is it to create a C2?\n";
{ C2 c2(7); }
}
输出:
How expesive is it to create a C1?
A::A()
A::(int)
A::operator=(const A&)
How expensive is it to create a C2?
A::(int)
Thus, the cost of not using an initializer is: one default constructor, one int constructor, and one assignment operator.
对于赋值操作,a = i
,构造函数A()
生成左值a
,构造函数A(int)
生成右值i
,然后赋值。如果使用成员列表初始化,则直接调用构造函数A(int)
。
不能使用成员列表的情况
class A {
public:
A(int i) : n_(i){}
protected:
int n_;
};
class B : public A {
public:
B() : n_(0)
{}
};
编译后报错:
error: class 'B' does not have any field named 'n_'
虽然基类A
的成员n_
可以被派生类B
继承,但是不能通过成员列表初始化。派生类的构造函数中,并不直接初始化基类的成员,而是调用基类的构造函数初始化基类的部分。
派生类B
首先会调用基类A
的构造方法,成员n_
在基类中被初始化,随后如果在派生类B
的成员列表初始化,那么同一个对象就会被初始化两次,所以基类的成员在派生类的初始化阶段是不可见的。
正确写法:
class A {
public:
A(int i) : n_(i){}
protected:
int n_;
};
class B : public A {
public:
B() : A(0) {}
};