C++继承(3) - 多重继承

目录

1.多重继承中构造函数的调用顺序

2.菱形继承问题

2.1问题介绍

2.2解决方法

2.3默认构造函数问题

3.总结


1.多重继承中构造函数的调用顺序

多重继承是C++的一个特性,使得一个类可以继承自多个类。子类会根据所继承的类的顺序,来依次调用父类的构造函数。

例如,下面程序中,B的构造函数先于A的构造函数被调用。

#include<iostream>
using namespace std;
 
class A
{
public:
  A()  { cout << "A constructor called" << endl; }
  ~A()  { cout << "A destructor called" << endl; }
};
 
class B
{
public:
  B()  { cout << "B constructor called" << endl; }
  ~B()  { cout << "B destructor called" << endl; }
};
 
class C: public B, public A  //注意这里的继承顺序
{
public:
  C()  { cout << "C constructor called" << endl; }
  ~C()  { cout << "C destructor called" << endl; }
};
 
int main()
{
    C c;
    return 0;
}

输出:
B constructor called
A constructor called
C constructor called
C destructor called
A destructor called
B destructor called

注意:析构函数的调用顺序与构造函数相反。

2.菱形继承问题

2.1问题介绍

菱形继承的问题出现在某个类的两个父类拥有共同的基类。

例如,下面程序中,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类的两份数据成员,这会导致歧义。

2.2解决方法

使用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

上述程序中,Person类的构造函数只调用了一次。

2.3默认构造函数问题

需要注意非常重要的一点:2.2中例子,结果显示对象ta1调用的是Person的默认构造函数。
当使用virtual关键字时,默认调用的是祖父类的默认构造函数。即使父类显式地调用了祖父类的带参数的构造函数,也不会改变这个行为。
而如果我们把Person的默认构造函数注释起来,则编译时会报错。
visual studio2015报错如下:
error C2512: “Person::Person”: 没有合适的默认构造函数可用
g++ 4.8.5 报错如下:
error: no matching function for call to 'Person::Person()'

如何才能调用Person的带参数的构造函数? 
必须在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), 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

3.总结

一般来说,子类不允许直接调用祖父类的构造函数,而应该通过父类来调用。仅当使用了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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值