C++ 多重继承

一,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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值