C++ 多重继承

来源:互联网 发布:梦想云进销存软件 编辑:程序博客网 时间:2024/06/06 02:56

一,C++多重继承

MI描述的是有多个直接基类的类。与单继承一样公有多继承表示也是is-a关系。

class Worker{private:    string name;public:    void show();};class Singer : public Worker{private:    int voice;public:    void show();};class Waiter : public Worker{private:    int panache;public:    void show();};class SingingWaiter : public Singer, public Waiter{public:    void show();};
 
二,多继承的优点与缺点

a,优点:容易实现多个接口。

b,缺点:比较复杂,会产生很多问题。


三,使用多继承带来的问题1 -- 从两个或更多相关基类那里继承同一个类的多个实例

1,从两个或更多相关基类那里继承同一个类的多个实例

class SingingWaiter : public Singer, public Waiter
因为Singer与Waiter都继承了一个Worker组件,因此SingingWaiter将包含两个worker组件。此时,如果将派生类对象的地址赋给基类指针,将出现二义性。

SingingWaiter obj;Worker *p = &obj;
上面的这种赋值将把指针设置为派生类对象中的基类对象的地址,但obj对象中包含两个Worker对象,有两个地址可供选择,所以应使用类型转换来指定对象。

SingingWaiter obj;Worker *p1 = (Singer *)&obj;Worker *p2 = (Waiter *)&obj;
 

2, 使用虚基类解决上面的问题

C++引入多重继承的同时引入虚基类,虚基类使得从多个类(它们的基类相同)派生出的类的对象只继承一个基类对象。例如,通过在类声明中使用virtual关键字,可以使Worker被用作Singer与Waiter的虚基类(virtual与public的次序无关紧要)。

class Singer : virtual public Worker{...};class Waiter : virtual public Worker{...};class SingingWaiter : public Singer, public Waiter{...};
现在,SingingWaiter对象将包含Worker对象的一个副本。


3,新的构造函数规则

class A{private:    int a;public:    A(int n):a(n){}};class B : public A{private:    int b;public:    B(int n, int m):A(n), b(m){}};
创建一个B类对象时,B类的构造函数只能调用A类的构造函数,B类的构造函数使用值m,并将值n传递给A类的构造函数。如果Worker是虚基类,则这种自动信息传递将不起作用。例如,对于下面的构造函数:

SingingWaiter(Worker &wk, int p, int v):Waiter(wk, p), Singer(wk, v){}
存在的问题是,自动信息传递时,将通过两条不同的途径(Waiter与Singer)将wk传递给Worker对象。为了避免这种冲突,C++在基类是虚的时,禁止信息通过中间类自动传递给基类。因此上述构造函数将初始化panache与voice,但wk参数中的信息将不会传递给子对象Waiter。然而,编译器必须在构造派生类对象之前构造基类对象组件。在上述情况下,编译器将使用Worker的默认构造函数。如果不希望使用默认构造函数来构造虚基类对象,则需要显示调用所需的基类构造函数。因此,构造函数应该是这样的。

SingingWaiter(Worker &wk, int p, int v):Worker(wk), Waiter(wk, p), Singer(wk, v){}
上面的代码显示调用Worker类的构造函数,这种做法是合法的,对于虚基类。但是对于非基类,则是非法的。如果是非基类,在SingingWaiter中只能调用Waiter与Singer的构造函数。


,使用多继承带来的问题2 -- 函数调用的二义性

1,多继承导致的函数调用二义性

例如,SingingWaiter类可能从Singer与Waiter类哪里继承了两个不同的show()函数。

SingingWaiter obj;obj.show();
对于单继承,如果没有重新定义show函数,则将使用最近祖先中的定义。而在多重继承中,每个直接祖先都有一个show函数,这将导致函数调用二义性。


2,使用作用域解析运算符来澄清编程者的意图

SingingWaiter obj;obj.Singer::show();obj.Waiter::show();
更好的方法是在SingingWaiter中重新定义show,并指出要使用哪个show。

void SingingWaiter::show(){    Singer::show();}


3,对于单继承,让派生类方法调用基类方法是可以的,然而这种递增方法对SingingWaiter无效。

下面的做法忽略了Waiter组件。

void SingingWaiter::show(){    Singer::show();}

通过同时调用Waiter版本的show函数来弥补。

void SingingWaiter::show(){    Singer::show();    Waiter::show();}
然而,这样做将显示Worker类对象信息两次,因为Singer::show()与Waiter::show()都调用了Worker::show()。要解决上面的问题,可以使用模块化方法,而不是递增方式,即提供一个只显示Worker组件的方法,一个只显示Singer组件的方法,一个只显示Waiter组件的方法。

void Worker::data(){    cout<<"Name: "<<name<<endl;    cout<<"ID: "<<id<<endl;}void Waiter::Data(){    cout<<"Panache: "<<panache<<endl;}void Singer::Data(){    cout<<"voice: "<<voice<<endl;}void SingingWaiter::Data(){    Singer::Data();    Waiter::Data();}void SingingWaiter::show(){    Worker::Data();    Data();}
0 0
原创粉丝点击