多重继承MI

本文探讨了多重继承带来的问题,如同名方法冲突和基类实例重复,并介绍了虚基类的概念及其如何解决这些问题。通过具体例子展示了如何在C++中正确地实现多重继承。

多重继承

多重继承又被称为MI,就是继承多个类.

这会引起很多问题,主要有两问题:

  1. 从多个不同基类继承的同名方法

  2. 从多个类继承相同类实例

设计一个类

在这里插入图片描述

类设计图如上所示,定义一个Worker类, Singer类和Waiter类是Worker类的派生类,SiningWaiter继承了Waiter和Singer两个类.

类设计如下

首先定义Worker,Singer,Waiter类

class Worker   // 这是一个ABC类
	{
	private:
	    std::string fullname;
	    long id;
	public:
	    Worker() : fullname("no one"), id(0L) {}
	    Worker(const std::string & s, long n)
		    : fullname(s), id(n) {}
	    virtual ~Worker() = 0;   // 纯虚函数
	    virtual void Set();
	    virtual void Show() const;
	};

	class Waiter : public Worker
	{
	private:
	    int panache;
	public:
	    Waiter() : Worker(), panache(0) {}
	    Waiter(const std::string & s, long n, int p = 0)
		    : Worker(s, n), panache(p) {}
	    Waiter(const Worker & wk, int p = 0)
		    : Worker(wk), panache(p) {}
	    void Set();
	    void Show() const;
	};

	class Singer : public Worker
	{
	protected:
	    enum {other, alto, contralto, soprano,
		            bass, baritone, tenor};
	    enum {Vtypes = 7};
	private:
	    static char *pv[Vtypes];    // 静态字符类型指针数组
	    int voice;
	public:
	    Singer() : Worker(), voice(other) {}
	    Singer(const std::string & s, long n, int v = other)
		    : Worker(s, n), voice(v) {}
	    Singer(const Worker & wk, int v = other)
		    : Worker(wk), voice(v) {}
	    void Set();
	    void Show() const;
	};

接口实现如下:

Worker::~Worker() {}		// 虚析构函数必须实现

	void Worker::Set()
	{
	    cout << "Enter worker's name: ";
	    getline(cin, fullname);
	    cout << "Enter worker's ID: ";
	    cin >> id;
	    while (cin.get() != '\n')
		continue;
	}

	void Worker::Show() const
	{
	    cout << "Name: " << fullname << "\n";
	    cout << "Employee ID: " << id << "\n";
	}

	// Waiter接口实现
	void Waiter::Set()
	{
	    Worker::Set();
	    cout << "Enter waiter's panache rating: ";
	    cin >> panache;
	    while (cin.get() != '\n')
		continue;
	}

	void Waiter::Show() const
	{
	    cout << "Category: waiter\n";
	    Worker::Show();
	    cout << "Panache rating: " << panache << "\n";
	}

	// Singer接口实现
	char * Singer::pv[] = {"other", "alto", "contralto",
		    "soprano", "bass", "baritone", "tenor"};

	void Singer::Set()
	{
	    Worker::Set();
	    cout << "Enter number for singer's vocal range:\n";
	    int i;
	    for (i = 0; i < Vtypes; i++)
	    {
		cout << i << ": " << pv[i] << "   ";
		if ( i % 4 == 3)
		    cout << endl;
	    }
	    if (i % 4 != 0)
		cout << endl;
	    while (cin >>  voice && (voice < 0 || voice >= Vtypes) )
		cout << "Please enter a value >= 0 and < " << Vtypes << endl;
	    while (cin.get() != '\n')
		continue;
	}

	void Singer::Show() const
	{
	    cout << "Category: singer\n";
	    Worker::Show();
	    cout << "Vocal range: " << pv[voice] << endl;
	}

以上是很简单的但继承实例,Singer和Waiter直接从Worker继承了方法和数据.

但是如果从Singer和Waiter继承类,会导致以下问题:

  1. 因为Singer和Waiter都是从Worker类继承的,但是SiningWaiter继承了Singer和Waiter类中的两个Worker类怎么解决?

  2. 同名方法怎么解决

解决多继承的方法

class SiningWaiter : public Singer, public Waiter{ .... }

以上多继承导致的问题解决如下:

多个Worker基类出现的问题如下:

SingWaiter ed;
Worker * pw = &ed;

通常,&ed中有两个Worker的地址,选择哪一个呢?产生二义性.通过强制转换解决:

Worker * pw1 = (Waiter * )&ed;
Worker * pw2 = (Singer * )&ed;

但是这也使基类指针变得复杂化.而且需要把SiningWaiter同其他Worker的派生类一样,所以不需要这种方法.

所以C++有一中共享基类的方法,虚基类.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GPglJ7IE-1590412991421)(2020-01-05_14-00.png)]

在上图设计中,让Waiter和Singer共享一个Worker基类对象,Worker被称为虚基类,声明如下:

class Singer : virtual public Worker {...};
class Waiter: public virtual Worker {...};

virtual和public 没有顺序之分

这样声明类,Singer和Waiter中的共享一个Worker对象,也需要重新编写构造函数规则.

SiningWaiter(const Worker & wk ,int p = 0,int v = Singer :: other)
			: Waiter(wk,p),Singer(wk,v)	{}

在上述这种情况下,wk是不允许通过Worker或者Singer传入Worker中,或者说编译器不知道从那个传,所以在这种情况下,会自动调用Worker的默认构造函数.

不想调用默认构造函数,就要显示调用Worker默认构造函数

SiningWaiter(const Worker & wk ,int p = 0,int v = Singer :: other)
			: Worker(wk) , Waiter(wk,p),Singer(wk,v)	{}

上述做法只有在虚基类下才合法.

同名方法

在单继承中,类中没有重新定义方法,就会优先使用最近的祖先类方法,而上述情况,多继承的情况下,SiningWaiter调用show方法会导致二义性,不知道调用Worker还是Singer的show方法.

采取域解析运算符

void SiningWaiter:: show()
{
	Singer :: show();
	Waiter :: show();
} 

然而,Worker的show和Singer的show可能有重复的部分,所以采用模块化,在protrected中设计一个值显示Worker组件,Waiter组件,Singer组件的方法,然后在SiningWaiter方法中组合起来.如下:

 void Worker::Data() const
	{
	    cout << "Name: " << fullname << endl;
	    cout << "Employee ID: " << id << endl;
	}

	void Waiter::Data() const
	{
	    cout << "Panache rating: " << panache << endl;
	}

	void Singer::Data() const
	{
	    cout << "Vocal range: " << pv[voice] << endl;
	}

	void SingingWaiter::Show() const
	{
	    cout << "Category: singing waiter\n";
	    Worker::Data();
	    Waiter::Data();
	    Singer::Data();
	}

利用这种模块化的方法和重新定义的方法,解决了不同方法的问题.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值