多重继承
多重继承又被称为MI,就是继承多个类.
这会引起很多问题,主要有两问题:
-
从多个不同基类继承的同名方法
-
从多个类继承相同类实例
设计一个类

类设计图如上所示,定义一个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继承类,会导致以下问题:
-
因为Singer和Waiter都是从Worker类继承的,但是SiningWaiter继承了Singer和Waiter类中的两个Worker类怎么解决?
-
同名方法怎么解决
解决多继承的方法
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();
}
利用这种模块化的方法和重新定义的方法,解决了不同方法的问题.
本文探讨了多重继承带来的问题,如同名方法冲突和基类实例重复,并介绍了虚基类的概念及其如何解决这些问题。通过具体例子展示了如何在C++中正确地实现多重继承。
72

被折叠的 条评论
为什么被折叠?



