条款 40 :明智而省慎地使用多重继承
Use multiple inheritance judiciously.
多重继承是C++的一个特色。对于多重继承,C++社群也有两种观点,一种认为多重继承是好的,另一种则认为应不要使用多重继承。
我们来看一段多重继承的代码:
class BorrowableItem{//图书馆允许你借某些东西
public:
void checkOut();//离开时进行检查
...
};
class ElectronicGadget{
private:
bool checkOut()const;//自我检测,成功则返回
};
class MP3Player://注意这里的多重继承
public BorrowableItem,
public ElectronicGadget
{...};//这里定义不是我们关心的重点
MP3Player mp;
mp.checkOut();//这里有歧义,到底该调用哪一个checkOut?
对于上述情况:
C++判别方式与解析重载函数规则一样:找到最佳匹配。(本例中两个函数匹配度下相同,这也是出现起义的原因)。为了解决这个问题你必须指明到底调用哪一个函数,例如BorrowableItem::check().
我们再来看一个多重继承代码:
class File{...};
class InFile:public File{...};
class OutFile:public File{...};
class IOFile:public InFile,public OutFile{...};
任何时候你的继承体系中某个base class到某个derived class存在一条以上路径(例如上述File->IOFile就有两条路),那么你就要是不是要让base class 内的成员变量经过每一条路径被复制。例如假设File存在一个内部变量filename,从继承角度来看,IOFile应该有两份filename(一份继承自InFile,一份继承自OutFile)(观点1)。但是事实告诉我们不应该有两份 filename(观点2)。
奇怪的是,C++对于这两种观点,它都支持,其默认做法是属于第一种观点。如果你想要实现观点,必须要到虚继承:
class File{...};
class InFile:virtual public File{...};
class OutFile:virtual public File{...};
class IOFile:public InFile,public OutFile{...};
所以照正确的观点来说 ,public继承总是virtual public继承。也就是说当你需要public继承,都应改成用virtual public继承。事实是这样吗?显然不是的!因为通过virtual继承而来的对象会更大,访问会更慢。总之你的编译器会为virtual付出相应代价。还有更复杂的是,virtual base的初始化责任由继承体系的最底层 derived class负责。(相当于自底向上策略,而常规更简单的是自顶向下策略)。
当然多重继承体系在很多情况下是有用的。但是对于多重继承的建议是:多重继承只是一个工具而已,它相比单继承更加复杂,更加难以理解,所以能用多继承则尽量不用多重继承。
请记住
- 多重继承比单一继承复杂。他可能导致心得歧义,以及对virtual继承的需要。
- virtual继承会增加大小,速度,初始化(及赋值)复杂度等等成本。如果virtual base class不带任何数据,将是最有价值的情况。
- 多重继承确实有正当用途。