1.多重继承中构造函数的调用顺序
多重继承是C++的一个特性,使得一个类可以继承自多个类。子类会根据所继承的类的顺序,来依次调用父类的构造函数。
例如,下面程序中,B的构造函数先于A的构造函数被调用。
B's constructor called
A's constructor called
C's constructor called
#include<iostream> using namespace std; class A { public: A() { cout << "A's constructor called" << endl; } }; class B { public: B() { cout << "B's constructor called" << endl; } }; class C: public B, public A //注意这里的继承顺序 { public: C() { cout << "C's constructor called" << endl; } }; int main() { C c; return 0; }输出:
B's constructor called
A's constructor called
C's constructor called
析构函数的调用顺序与构造函数相反。
2.菱形(钻石)继承问题
菱形继承的问题出现在某个类的两个父类拥有共同的基类。
例如,下面程序中,TA类会拥有Person类的两份数据成员拷贝,这会导致歧义性。
参考下面程序:
#include<iostream> using namespace std; class Person { public: Person(int x) { cout << "Person::Person(int) called" << endl; } }; class Faculty : public Person { public: Faculty(int x):Person(x) { cout<<"Faculty::Faculty(int) called"<< endl; } }; class Student : public Person { public: Student(int x):Person(x) { cout<<"Student::Student(int) called"<< endl; } }; class TA : public Faculty, public Student { public: TA(int x):Student(x), Faculty(x) { cout<<"TA::TA(int) called"<< endl; } }; int main() { TA ta1(30); }
运行结果:
Person::Person(int ) called
Faculty::Faculty(int ) called
Person::Person(int ) called
Student::Student(int ) called
TA::TA(int ) called
上述程序中,Person类的构造函数调用了两次。当对象ta1被销毁时,Person的析构函数也会被调用两次。 所以对象ta1′拥有Person类的两份数据成员, 这会导致歧义。
解决方法是使用virtual关键字. 将Faculty和Student做为virtual 基类来避免TA类中出现Person的两份拷贝。参考下面程序:
#include<iostream> using namespace std; class Person { public: Person(int x) { cout << "Person::Person(int ) called" << endl; } Person() { cout << "Person::Person() called" << endl; } }; class Faculty : virtual public Person { public: Faculty(int x):Person(x) { cout<<"Faculty::Faculty(int ) called"<< endl; } }; class Student : virtual public Person { public: Student(int x):Person(x) { cout<<"Student::Student(int ) called"<< endl; } }; class TA : public Faculty, public Student { public: TA(int x):Student(x), Faculty(x) { cout<<"TA::TA(int ) called"<< endl; } }; int main() { TA ta1(30); }
输出结果:
Person::Person() called
Faculty::Faculty(int ) called
Student::Student(int ) called
TA::TA(int ) called
Faculty::Faculty(int ) called
Student::Student(int ) called
TA::TA(int ) called
上述程序中,‘Person’ 的构造函数只调用了一次。
需要注意非常重要的一点:调用的是Person的默认构造函数。当使用virtual关键字时,默认调用的是祖父类的默认构造函数。即使父类显式地调用了祖父类的带参数的构造函数,也不会改变这个行为。
而如果我们把Person的默认构造函数注释起来,则编译时会报错。visual studio2015报错如下:
error C2512: “Person::Person”: 没有合适的默认构造函数可用
如何才能调用Person的带参数的构造函数?
必须在TA类中调用Person的带参数的构造函数。如下所示:
Person::Person(int ) called
Faculty::Faculty(int ) called
Student::Student(int ) called
TA::TA(int ) called
#include<iostream> using namespace std; class Person { public: Person(int x) { cout << "Person::Person(int ) called" << endl; } Person() { cout << "Person::Person() called" << endl; } }; class Faculty : virtual public Person { public: Faculty(int x):Person(x) { cout<<"Faculty::Faculty(int ) called"<< endl; } }; class Student : virtual public Person { public: Student(int x):Person(x) { cout<<"Student::Student(int ) called"<< endl; } }; class TA : public Faculty, public Student { public: TA(int x):Student(x), Faculty(x), Person(x) { cout<<"TA::TA(int ) called"<< endl; } }; int main() { TA ta1(30); }输出:
Person::Person(int ) called
Faculty::Faculty(int ) called
Student::Student(int ) called
TA::TA(int ) called
总体上来说,不允许直接调用祖父类的构造函数,而应该通过父类来调用。仅当使用了virtual时,才能调用祖父类构造函数。
参考例子1:
#include<iostream> using namespace std; class A { int x; public: void setX(int i) {x = i;} void print() { cout << x; } }; class B: public A { public: B() { setX(10); } }; class C: public A { public: C() { setX(20); } }; class D: public B, public C { }; int main() { D d; d.print(); return 0; }编译错误。类D中的print()函数有歧义。
visual studio2015编译报错: error C2385: 对“print”的访问不明确,note: 可能是“print”(位于基“A”中),note: 也可能是“print”(位于基“A”中)
将类B和C设置为虚继承后(如下面两行所示),则输出结果为20.
class B: virtual publicA
class C: virtual publicA
参考例子2
#include<iostream> using namespace std; class A { int x; public: A(int i) { x = i; } void print() { cout << x; } }; class B: virtual public A { public: B():A(10) { } }; class C: virtual public A { public: C():A(10) { } }; class D: public B, public C { }; int main() { D d; d.print(); return 0; }
输出:
编译错误。类A中没有定义默认构造函数。
当给A加上默认构造函数后:
A() { x = 11; }
则输出结果为: 11