【Exceptional C++(13)】使用/滥用继承
2018-01-29 本文已影响11人
downdemo
问题
- 耦合关系要尽量减少,如果class和class之间有多种方式表达,使用关系最弱的一种,继承是C++所能表达的最强烈关系,只有在没有性能相近的更弱关系时才使用继承
- 以下template提供了list的管理功能,包括特定list位置上处理元素的能力
// Example 1
template <class T>
class MyList {
public:
bool Insert(const T&, size_t index);
T Acess(size_t index) const;
size_t Size() const;
private:
T* buf;
size_t bufsize;
};
- 考虑下面代码,以MyList为基础实现一个MySet
template <class T>
class MySet1 : private MyList<T>
{
public:
bool Add(const T&); // call Insert()
T Get(size_t index) const;
// call Acess()
using MyList<T>::Size;
...
};
template <class T>
class MySet2
{
public:
bool Add(const T&); // call impl_.Insert()
T Get(size_t index) const;
// call impl_.Acess()
size_t Size() const; // call impl_.Size()
...
private:
MyList<T> impl_;
};
- MySet1和MySet2有区别吗?nonpublic inheritance和containment有什么不同?
解答
- 两者没有实际意义上的区别,因为它们功能相同
- nonpublic inheritance应该总是表现出is implemented in terms of的意义,containment总是表现出has a的意义,连带也有is implemented in terms of的意义。可见inheritance是single containment的一个超集,可以用MyList<T> member完成的都可以用继承自MyList<T>完成。inheritance只能拥有一份MyList<T>作为base subobject,如果需要拥有多份MyList<T>实体,就要用containment
- 尽量用aggregation(也叫composition、layering、has-a、delegation)取代inheritance
- 需要使用inheritance的理由
- 需要重写虚函数,这是inheritance的经典使用理由
- 需要处理protected member
- 需要在一个base subobject之前构建used object,或在之后摧毁used object
- 需要分享某个共同的虚基类或重写虚基类的构建程序
- 从只有函数没有成员变量的empty base class继承不会增加空间负担,能获得最佳收益
- 需要实现受限制的多态,即LSP ISA,一个受到束缚的IS-A关系
- 分析MySet看看是否适用上述标准
- MyList没有protected member,所以不需要继承来存取
- MyList没有虚函数,不需要继承重写
- MyList没有其他基类,所以MyList对象不需要在另一个basesubobject之前构建或之后销毁
- MyList没有虚基类是需要共享的或construction需要重写
- MyList不是空的,不适用empty base class最佳化
- MySet不是一个IS-NOT-A MyList
- 所以MySet不应继承MyList,下面重写MySet2,以更广泛的方式使用containment,不只是以MyList<T>为基础具现,标准库就是用这项技术来完成stack和deque两个template的
template <class T, class Impl = MyList<T>>
class MySet3
{
public:
bool Add(const T&); // call impl_.Insert()
T Get(size_t index) const;
// call impl_.Acess()
size_t Size() const; // call impl_.Size()
...
private:
Impl impl_;
};