您的位置:首页 > 编程语言 > C语言/C++

Effective C++笔记: 继承和面向对象设计(四)

2009-07-17 18:04 246 查看
 

Item 40: 谨慎使用 multiple inheritance(多继承)
multiple inheritance(多继承)意味着从多于一个的 base class(基类)继承, 可能会导致较多的歧义。
成员函数歧义:
class BorrowableItem {             // something a library lets you borrow
public:
  void checkOut();                 // check the item out from the library
  ...
};
class ElectronicGadget {
private:
  bool checkOut() const;           // perform self-test, return whether
  ...                              // test succeeds
};
class MP3Player:                   // note MI here
  public BorrowableItem,           // (some libraries loan MP3 players)
  public ElectronicGadget
{ ... };                           // class definition is unimportant
MP3Player mp;
mp.checkOut();                     // ambiguous! which checkOut?
注意这个例子,即使两个函数中只有一个是可访问的,对 checkOut 的调用也是有歧义的。(checkOut 在 BorrowableItem 中是 public(公有)的,但在 ElectronicGadget 中是 private(私有)的。)这与 C++ 解析 overloaded functions(重载函数)调用的规则是一致的:在看到一个函数的是否可访问之前,C++ 首先确定与调用匹配最好的那个函数。只有在确定了 best-match function(最佳匹配函数)之后,才检查可访问性。(注意,因此访问权限不能区分不同的函数)这目前的情况下,两个 checkOuts 具有相同的匹配程度,所以就不存在最佳匹配。因此永远也不会检查到 ElectronicGadget::checkOut 的可访问性。
为了消除歧义性,你必须指定哪一个 base class(基类)的函数被调用:
mp.BorrowableItem::checkOut();              // ah, that checkOut...
当然,你也可以尝试显式调用 ElectronicGadget::checkOut,但这样做会有一个 "you're trying to call a private member function"(你试图调用一个私有成员函数)错误代替歧义性错误。
 
成员变量的歧义:(采用虚拟继承消除)
class File { ... };
class InputFile: public File { ... };
class OutputFile: public File { ... };
class IOFile: public InputFile,
              public OutputFile
{ ... };
你拥有一个“在一个 base class(基类)和一个 derived class(派生类)之间有多于一条路径的 inheritance hierarchy(继承体系)”(就像上面在 File 和 IOFile 之间,有通过 InputFile 和 OutputFile 的两条路径)的任何时候,你都必须面对是否需要为每一条路径复制 base class(基类)中的 data members(数据成员)的问题。例如,假设 File class 有一个 data members(数据成员)fileName。IOFile 中应该有这个 field(字段)的多少个拷贝呢?一方面,它从它的每一个 base classes(基类)继承一个拷贝,这就暗示 IOFile 应该有两个 fileName data members(数据成员)。另一方面,简单的逻辑告诉我们一个 IOFile object(对象)应该仅有一个 file name(文件名),所以通过它的两个 base classes(基类)继承来的 fileName field(字段)不应该被复制。
C++ 在这个争议上没有自己的立场。它恰当地支持两种选项,虽然它的缺省方式是执行复制。如果那不是你想要的,你必须让这个 class(类)带有一个 virtual base class(虚拟基类)的数据(也就是 File)。为了做到这一点,你要让从它直接继承的所有的 classes(类)使用 virtual inheritance(虚拟继承):
class File { ... };
class InputFile: virtual public File { ... };
class OutputFile: virtual public File { ... };
class IOFile: public InputFile,
              public OutputFile
{ ... };
从正确行为的观点看,public inheritance(公有继承)应该总是 virtual(虚拟)的。但虚拟继承会付出额外的代价,因此,除非必需,否则不要使用 virtual bases。缺省情况下,使用 non-virtual继承。第二,如果你必须使用 virtual base classes(虚拟基类),试着避免在其中放置数据。这样你就不必在意它的 initialization(初始化)和赋值所带来的问题。(初始化一个 virtual base(虚拟基)的职责由 hierarchy(继承体系)中 most derived class(层次最低的派生类)承担。这个规则中包括的含义:(1) 从需要 initialization(初始化)的 virtual bases(虚拟基)派生的 classes(类)必须知道它们的 virtual bases(虚拟基),无论它距离那个 bases(基)有多远;(2) 当一个新的 derived class(派生类)被加入继承体系时,它必须为它的 virtual bases(虚拟基)(包括直接的和间接的)承担 initialization responsibilities(初始化职责))。
 
一个采用多重继承的例子:(注意为什么此处采用多重继承)
IPerson是一个接口类,公有继承;
PersonInfo是一个帮助实现的工具类,因为要重新定义其中的virtual函数,因此采用private继承。(参见条款39)
class IPerson {                            // this class specifies the
public:                                    // interface to be implemented
  virtual ~IPerson();
  virtual std::string name() const = 0;
  virtual std::string birthDate() const = 0;
};
class DatabaseID { ... };                  // used below; details are
                                           // unimportant
class PersonInfo {                         // this class has functions
public:                                    // useful in implementing
  explicit PersonInfo(DatabaseID pid);     // the IPerson interface
  virtual ~PersonInfo();
  virtual const char * theName() const;
  virtual const char * theBirthDate() const;
  virtual const char * valueDelimOpen() const;
  virtual const char * valueDelimClose() const;
  ...
};
 
class CPerson: public IPerson, private PersonInfo {   // note use of MI
public:
  explicit CPerson(    DatabaseID pid): PersonInfo(pid) {}
  virtual std::string name() const           // implementations
  { return PersonInfo::theName(); }          // of the required
                                             // IPerson member
  virtual std::string birthDate() const       // functions
  { return PersonInfo::theBirthDate(); }
private:        
// redefinitions of inherited virtual delimiter functions
  const char * valueDelimOpen() const { return ""; }   
  const char * valueDelimClose() const { return ""; }  
};
 
总结:
multiple inheritance(多继承)比 single inheritance(单继承)更复杂。它可能导致新的歧义问题和对 virtual inheritance(虚拟继承)的需要。
virtual inheritance(虚拟继承)增加了 size(大小)和 speed(速度)成本,以及 initialization(初始化)和 assignment(赋值)的复杂度。当 virtual base classes(虚拟基类)没有数据成员时,它是最适用的。
multiple inheritance(多继承)合理的用途:”public继承某个Interface class(接口类)”和”private inheritance(私有继承)某个协助实现的class”的组合。
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息