文章目录
多重继承(MI)
- 公有MI表示的是is-a关系
- 私有MI和保护MI表示的是has-a关系
这里主要介绍公有MI
公有MI主要解决的几个问题:
- 从不同的基类继承多个相同的祖先对象成员------->使用虚继承使只继承一个祖先对象
- 从两个不同的基类继承同名方法-------->采用模块化方式而不是递增的方式编程,根据需要选择不同模块组合成新的方法。
- 从两个或更多相关基类那里继承同一个类的多个实例------->混合使用虚基类和非虚基类,会导致包含一个虚途经子对象和多个非虚途径子对象
虚基类需要使用新的构造函数规则
// multiple inheritance
class SingingWaiter : public Singer, public Waiter
{
protected:
void Data() const;
void Get();
public:
SingingWaiter() {}
SingingWaiter(const std::string &s, long n, int p = 0,
int v = other)
: Worker(s, n), Waiter(s, n, p), Singer(s, n, v) {}
// KP
// 这里存在的问题是,在自动传递信息时,C++将通过两条不同的途径waiter和singer将wk信息传递给worker
// 为了避免这种冲突,C++在基类是虚的时,将禁止信息通过中间类自动传递给基类
// 这里 Waiter(s, n, p), Singer(s, n, v)将只能初始化中间类的新增成员p和v,而对基类成员不做处理
// 这种情况下,需要自己额在成员初始化列表额外的显示调用所需的基类构造函数
// 否则,这里默认将采用基类的默认构造函数
// FAO 对于虚基类,这样做是合法且必须的;而对于非虚基类,这样则是非法的
SingingWaiter(const Worker &wk, int p = 0, int v = other)
: Worker(wk), Waiter(wk, p), Singer(wk, v) {}
SingingWaiter(const Waiter &wt, int v = other)
: Worker(wt), Waiter(wt), Singer(wt, v) {}
SingingWaiter(const Singer &wt, int p = 0)
: Worker(wt), Waiter(wt, p), Singer(wt) {}
void Set();
void Show() const;
}
MI每个直接祖先继承自祖宗(一般是一个抽象基类),拥有相同的方法接口(实现可能不同),这使得上述方法的调用是有二义性的。
解决方法:
1. 使用作用域解析运算符
2. 更好的方法是在孙子中使用 模块化方式(配合保护访问方式) 重新定义该方法,并指出要使用哪个show()
class Worker // an abstract base class
{
private:
std::string fullname;
long id;
protected:
//KP
// 采用模块化方式而不是递增方式避免数据的重复调用
// 这里用作协助公有接口的辅助方法
// 采用保护方式避免使得这些方法只能在继承结构层次类使用他们
virtual void Data() const; //worker数据的输入
virtual void Get(); //worker数据的展示
public:
//这里的两个方法调用了上面的保护辅助方法
virtual void Set() = 0;
virtual void Show() const = 0;
};
class Waiter : virtual public Worker
{
private:
int panache;
protected:
void Data() const;
void Get();
public:
// Waiter methods
// 这里使用Worker::Get()模块和派生类新增的Get模块来实现Set方法
void Waiter::Set()
{
cout << "Enter waiter's name: ";
// 输入worker部分
Worker::Get();
// 输入waiter增加部分
Get();
}
void Waiter::Show() const
{
cout << "Category: waiter\n";
// 展示worker部分
Worker::Data();
// 展示waiter部分
Data();
}
}
void Waiter::Show() const
{
cout << "Category: waiter\n";
// 展示worker部分
Worker::Data();
// 展示waiter部分
Data();
}
};
class Singer : virtual public Worker
{
protected:
void Data() const;
void Get();
private:
static char const *pv[Vtypes]; // string equivs of voice types
int voice;
public:
void Set();
void Show() const;
};
// multiple inheritance
class SingingWaiter : public Singer, public Waiter
{
protected:
void SingingWaiter::Data() const
{
Singer::Data();
Waiter::Data();
}
void SingingWaiter::Get()
{
Waiter::Get();
Singer::Get();
}
public:
// 分别使用了worker模块和waiter新增的和singer新增的
// 模块化编程方式
void SingingWaiter::Set()
{
cout << "Enter singing waiter's name: ";
Worker::Get();
// 模块化,包括了waiter和singer中各自新增的部分
Get();
}
void SingingWaiter::Show() const
{
cout << "Category: singing waiter\n";
Worker::Data();
Data();
}
};
继承中的同名成员处理:
问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
- 访问子类同名成员,直接访问即可(通过优先规则解决名称二义性)
- 访问父类同名成员,需要加作用域
总结:
- 子类对象可以直接访问子类中同名成员
- 子类对象加作用域可以访问到父类中同名成员
- 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名的成员函数,加作用域可以访问到父类中成员函数及其重载版本
- 非虚继承时,多个不同的类加类名限定即可。
问题:继承中同名的静态成员在子类对象上如何进行访问?
静态成员和非静态成员出现同名,处理方式一致,只不过有两种访问方式(通过对象 和 通过类名)
- 访问子类同名成员,直接访问即可
- 访问父类同名成员,需要加作用域