C++之多重继承

大多数应用程序使用单个基类的公用继承,但是在某些情况下,单继承是不够的,必须使用多继承。C++允许为一个派生类指定多个基类,这样的继承结构被称做多重继承.

举个例子,交通工具类可以派生出汽车和船连个子类,但拥有汽车和船共同特性水陆两用汽车就必须继承来自汽车类与船类的共同属性。如下图示:

代码实现:

//多重继承
#include <iostream>
using namespace std;
class Vehicle
{
public:
    Vehicle(int weight = 0)
    {
        Vehicle::weight = weight;
    }
    void SetWeight(int weight)
    {
        cout << "重新设置重量" << endl;
        Vehicle::weight = weight;
    }
    virtual void ShowMe() = 0;//纯虚函数
protected:
    int weight;
private:
};

class car :public Vehicle//汽车
{
public:
    car(int weight = 0, int aird = 0) :Vehicle(weight)
    {
        car::aird = aird;
    }
    void ShowMe()
    {
        cout << "我是汽车!" << endl;
    }
protected:
    int aird;
private:
};

class boat :public Vehicle//船
{
public:
    boat(int weight = 0, float tonnage = 0) :Vehicle(weight)
    {
        boat::tonnage = tonnage;
    }
    void ShowMe()
    {
        cout << "我是船!" << endl;
    }
protected:
    float tonnage;
private:
};

class AmphibianCar :public car, public boat//水陆两用汽车,多重继承的体现
{
public:
    AmphibianCar(int weight, int aird, float tonnage)
        :Vehicle(weight), car(weight, aird), boat(weight, tonnage)
        //多重继承要注意调用基类构造函数
    {

    }
    void ShowMe()
    {
        cout << "我是水陆两用汽车!" << endl;
    }
};

int main()
{
    AmphibianCar a(4, 200, 1.35f);//错误
    a.SetWeight(3);//错误
    system("pause");
}

上面的代码从表面看,看不出有明显的语发错误,但是它是不能够通过编译的。错误如下:
 


这有是为什么呢? 这是由于多重继承带来的继承的模糊性带来的问题。

先看如下的图示:

在图中深红色标记出来的地方正是主要问题所在,水陆两用汽车类继承了来自Car类与Boat类的属性与方法,Car类与Boat类同为AmphibianCar类的基类,在内存分配上AmphibianCar获得了来自两个类的SetWeight()成员函数,当我们调用a.SetWeight(3)的时候计算机不知道如何选择分别属于两个基类的被重复拥有了的类成员函数SetWeight()。

以上面的代码为例,我们要想让AmphibianCar类既获得一个Vehicle的拷贝,而且又同时共享用Car类与Boat类的数据成员与成员函数就必须通过C++所提供的虚拟继承技术来实现。

在Car类和Boat类继承Vehicle类时前面加上virtual关键字就可以实现虚拟继承,使用虚拟继承后,当系统碰到多重继承的时候就会自动先加入一个Vehicle的拷贝,当再次请求一个Vehicle的拷贝的时候就会被忽略,保证继承类成员函数的唯一性。

修改后的代码:

//加virtual后的正确代码
#include <iostream>  
using namespace std;

class Vehicle
{
public:
    Vehicle(int weight = 0)
    {
        Vehicle::weight = weight;
        cout << "载入Vehicle类构造函数" << endl;
    }
    void SetWeight(int weight)
    {
        cout << "重新设置重量" << endl;
        Vehicle::weight = weight;
    }
    virtual void ShowMe() = 0;
protected:
    int weight;
};
class Car :virtual public Vehicle//汽车,这里是虚拟继承  
{
public:
    Car(int weight = 0, int aird = 0) :Vehicle(weight)
    {
        Car::aird = aird;
        cout << "载入Car类构造函数" << endl;
    }
    void ShowMe()
    {
        cout << "我是汽车!" << endl;
    }
protected:
    int aird;
};

class Boat :virtual public Vehicle//船,这里是虚拟继承  
{
public:
    Boat(int weight = 0, float tonnage = 0) :Vehicle(weight)
    {
        Boat::tonnage = tonnage;
        cout << "载入Boat类构造函数" << endl;
    }
    void ShowMe()
    {
        cout << "我是船!" << endl;
    }
protected:
    float tonnage;
};

class AmphibianCar :public Car, public Boat//水陆两用汽车,多重继承的体现  
{
public:
    AmphibianCar(int weight, int aird, float tonnage)
        :Vehicle(weight), Car(weight, aird), Boat(weight, tonnage)
        //多重继承要注意调用基类构造函数  
    {
        cout << "载入AmphibianCar类构造函数" << endl;
    }
    void ShowMe()
    {
        cout << "我是水陆两用汽车!" << endl;
    }
    void ShowMembers()
    {
        cout << "重量:" << weight << "顿," << "空气排量:" << aird << "CC," << "排水量:" << tonnage << "顿" << endl;
    }
};
int main()
{
    AmphibianCar a(4, 200, 1.35f);
    a.ShowMe();
    a.ShowMembers();
    a.SetWeight(3);
    a.ShowMembers();
    system("pause");
}
 

输出:

虽然说虚拟继承与虚函数有一定相似的地方,但务必要记住,他们之间是绝对没有任何联系的!

************************************************************************************************************************

************************************************************************************************************************

总结多继承的一些要点和注意事项:

  • 多重继承的情况下,遇到二义性的可能将会更大,编译器不会试图根据派生类转换区别基类间的转换,转换成每个基类都一样好,有如下代码:

class ZooAnimal
{
};

class Bear : public ZooAnimal
{
};

class Endangered
{
};

class Panda : public Bear, public Endangered
{
};

如果有print函数的两个重载版本:

void print(const Bear&);

void print(const Endangered&);

这个调用将会出错,编译器将指出改掉用有二义性。

  • 假定在类的多个基类中定义了相同的成员,将会导致二义性,如果定义了同名的成员函数,即使是函数参数不同,也会导致二义性,代码如下:

class ZooAnimal
{

};

class Bear : public ZooAnimal
{
public:
    void print(int x) { cout << "print Bear" << endl; }

};

class Endangered
{
public:
    void print() { cout << "print Endangered" << endl; };
};

class Panda : public Bear, public Endangered
{
};

int main()
{
    Panda p;
    p.Bear::print(1);  //产生二义  p.print(1);
    p.Endangered::print();
    return 0;
}

输出结果

如果在ZooAnimal中定义了print 而Bear中没有定义,同样会出现二义性。避免二义性的最好方法就是指定函数的作用域,同上示例比如:

Bear::print(x);

猜你喜欢

转载自blog.csdn.net/yhc166188/article/details/81587968