Chapter 6. Inheritance and Object-Oriented Design
这一章围绕inhertance和OOD进行讨论。
Item 32:如果不是"is-a"关系,就一定不要用public inheritance,因为public inheritance就意味着base class的所有操作对于derived class完全适用。
Item 33:尽量不让base class和derived class拥有相同的变量名和函数名,你也不希望通过类似的声明去区分它们:
using base::func;
Item 34:pure virtual function一般只提供接口继承,虽然它也被可以使用。impure virtual function则用于提供接口和缺省实现。non-virtual function则提供接口和强制实现。
Item 35:本款主要介绍了用non-virtual interface、function pointer、tr1::function等实现的Template Method和Strategy模式,以替代virtual functions。
Item 36:不重新定义public inheritance的non-virtual function,一是为了保证"is-a"的纯洁关系,二是使invariant凌驾于specialization。
Item 37:为了避免dynamic binding的functions和static binding的default parameters在运行中的混淆,就不要重新定义这些default parameters。
Item 38:composition最简单、最直接的一种表现就是"has-a"关系了,而借助composition实现的"is-implementated-in-terms-of"则显得更为实用。
Item 39:由于private inheritance导致base class的接口和实现对derived class可见,却不能为other type class object所用,因此,composition + inheritance是更好地实现方式,EBO除外。
Item 40:MI/VI是很少被用到的,STL中的basic_iostream是一个典型了,如果有现成的classes可直接用于实现,MI用也无妨,只要你知道你在做什么。
Item 38 - 40
条款38:Model "has-a" or "is-implemented-in-terms-of" through composition
还记得"is-a"用public inheritence实现吧?composition有两种情况:"has-a"(application domain)和"is-implemented-in-terms-of"(implementation)。
"has -a"是很好区分的,而composition的另一实现,"is-implemented-in-terms-of"和"is-a"的区别则不明显。正 如Item 32中提到的,Square与Rectangle并非"is-a"关系,Set与List同样并非"is-a"关系,而是"is-implemented -in-terms-of"关系:
class Set {
public :
bool member(const T& item) const ;
void insert(const T& item);
void remove(const T& item);
std::size_t size() const ;
private :
std::list<T> rep; // representation for Set data
};
Things to Remember
1) Composition has meanings completely different from that of public inheritance.
2) In the application domain, composition means has-a. In the implementation domain, it means is-implemented-in-terms-of.
条款39:Use private inheritance judiciously
private inheritance并不表达"is-a"关系,而是"is-implemented-in-terms-of"关系,说白了,就是可以用base class的implenmentations实现derived class的implenmentations。当然,如果可能,composition是最好的选择。原因如下:
1) 解耦:为了实现"is-implemented-in-terms-of",以允许重写virtual functions为代价是private inheritance的硬伤。而根据需求composite一个public inheritance的derived class object则使得重写的private virtual functions对client透明。
2) 降低编译依存度:composition可以采用仅声明derived class的方式达到避免包含base class的head file的目的。
3) 例外:
class Empty {}; // has no data, so objects should use no memory
class HoldsAnInt { // should need only space for an int
private :
int x;
Empty e; // should require no memory
};
Empty对象默认占用1 byte,如果需要对齐,还不止于此。而:
class HoldsAnInt: private Empty {
private :
int x; // sizeof(HoldsAnInt) == sizeof(int)
};
这就是所谓的EBO(empty base optimization),EBO一般只在单一继承下才可行。
Things to Remember
1) Private inheritance means is-implemented-in-terms of. It's usually inferior to composition, but it makes sense when a derived class needs access to protected base class members or needs to redefine inherited virtual functions.
2) Unlike composition, private inheritance can enable the empty base optimization. This can be important for library developers who strive to minimize object sizes.
条款40:Use multiple inheritance judiciously
MI的一个可能的问题是abmiguity:
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?
由于两base classes的匹配程度相同,需明确指明是要调用哪一个:
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
再看:
...
protected :
string szFileName;
};
class InputFile: public File { ... };
class OutputFile: public File { ... };
class IOFile: public InputFile, public OutputFile
{ ... };
IOFile对象会有几个szFileName呢?解决办法就是令File成为virtual base class:
class InputFile: virtual public File { ... };
class OutputFile: virtual public File { ... };
class IOFile: public InputFile, public OutputFile
{ ... };
virtual inheritance的代价是:
1) 体积增大;
2) 访问成员变量速度变慢;
3) virtual base classes的初始化由most derived class负责。
因此,应尽量避免使用virtual inheritance,如果使用则应尽量避免在virtual base class中放置数据。
public :
virtual ~ IPerson();
virtual std::string name() const = 0 ;
virtual std::string birthDate() const = 0 ;
};
由于IPerson是abstract class无法被实例化,因此使用IPerson时,必须以pointers或references使用。这让我想到在Item 31中就曾用到的factory function:
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
此 时,当然还要有个继承自IPerson的derived class CPerson,并实现Iperson中的pure virtual functions,如果有现成的一个类PersonInfo可以实现相关功能,这不正意味着CPerson"is-implemented-in- terms-of"PersonInfo吗?
public :
explicit CPerson( DatabaseID pid): PersonInfo(pid) {}
virtual std::string name() const // implementations of the required IPerson member
virtual std::string birthDate() const // functions
{ return PersonInfo::theBirthDate(); }
private: // redefinitions of
const char * valueDelimOpen() const { return ""; } // inherited virtual
const char * valueDelimClose() const { return ""; } // delimiter
}; // functions
Things to Remember
1) Multiple inheritance is more complex than single inheritance. It can lead to new ambiguity issues and to the need for virtual inheritance.
2) Virtual inheritance imposes costs in size, speed, and complexity of initialization and assignment. It's most practical when virtual base classes have no data.
3) Multiple inheritance does have legitimate uses. One scenario involves combining public inheritance from an Interface class with private inheritance from a class that helps with implementation.