20180327 C++ 明智而审慎地使用多重继承
当多重继承(multiple inheritance,MI)运用到程序设计中时,程序有可能从一个以上的基类继承相同的名称(如函数,typedef等),那会导致较多的歧义,eg:
class BorrowableItem{//图书馆允许你借某些东西
public:
void checkOut();//离开时进行检查
...
};
class Company{
private:
bool checkOut() const;//进公司检查
...
};
class MP3Player:
public BorrowableItem,
public Company
{...};
MP3Player mp;
mp.checkOut();//歧义,到底调用的是哪个checkOut() ?
为了解决这个歧义,必须明白指出要调用的哪一个基类内的函数:
mp.BorrowableItem::checkOut();
当然也可以尝试明确调用Company()::checkOut(),但会获得“尝试调用私有成员函数”的错误。
多重继承的意思是 继承一个以上的基类,但这些基类常拥有更改机的基类,这很可能导致“钻石型多重继承”:
class File{...};
class InputFile:public File{...};
class OutputFile:public File{...};
class IOFile:public InPutFile,
public OutputFile
{...};
假设File类有个成员变量filename,那么IOFile内该有多少比这个名称的数据呢, C++缺省做法是IOFile从其每一个基类继承一份,所以其对象内有两份filename成员变量。
如果你不想那样做(即它继承自两个基类,而File那么不被复制),你需让带有此数据的类(即File)成为一个虚基类,所以要让所有直接继承自它的类采用“虚继承”:
class File{...};
class InputFile:virtual public File{...};
class OutputFile:virtual public File{...};
class IOFile:public InputFile,
public OutputFile
{...};
对虚基类(virtual base classes)(同样使用于虚继承)的忠告:
1)非必要不要使用虚基类(virtual base classes),平常请使用非虚(non-virtual)继承;
2)若必须使用虚基类(virtual base classes),尽量避免在其中放置数据。这样你就不需担心这些类身上的初始化(和赋值)所带来的诡异事情。
考虑下面这个用来形容(塑模,Modeling)人的C++ 接口类(Interface class);
class IPerson{
public:
virtual ~IPerson();
virtual std::string name() const = 0;
virtual std::string birthDate() const = 0;
};
IPerson的客户必须以IPerson的指针(pointers)和引用(references)来编写程序,因为抽象类无法被实体化创建对象,为了创建一些可被当做IPerson来使用的对象,IPerson的客户使用 工厂函数(factory functions)将“派生自IPerson的具象类”实体化:
//factory function(工厂函数),根据一个独一无二的数据库ID创建一个Person对象。
std::tr1::shared_ptr<IPerson> makePerson(DatabaseID personIdentifier);
//这个函数从使用者手上取得一个数据库ID
DatabaseID askUserForDatabaseID();
DatabaseID id(askUserForDatabaseID());
std::tr1::shared_ptr<IPerson> pp(makePerson(id));
//创建一个对象支持IPerson接口。
//借由IPerson成员函数处理*pp
...
/***********************看的一头雾水***************************/
注意:
1)多重继承比单一继承复杂,它可能导致新的歧义性,以及对虚继承的需要。
2)虚继承会增加大小、速度、初始化(及赋值)复杂度等成本。若虚基类不带任何数据,将是最具实用价值的情况。
3)多重继承的确有正当用途。其中一个用途可以适用到下面的情景: “公有继承摸个接口类(Interface class)”与“私有继承某个协助实现的类”的组合形式。