条款38 通过复合塑模出has-a或“根据某物实现出”
复合(composition)是类型之间的一关系,当某种类型的对象内含其它种类型的对象时,就是复合关系。
复合在实现域意味has-a(有一个)。在实现域,复合意味is-implemented-in-terms-of(根据某物实现出)
class Address{};
class PhoneNumber{};
class Person {
public:
...
private:
string name; //合成成分物
Address address; //合成成分物
PhoneNumber voiceNumber; //合成成分物
PhoneNumber faxNumber;//合成成分物
};
区分is-a(是一种)和is-implemented-in-terms-of(根据某物实现出)两种对象关系。
考虑一个template,制造出一组class用来表现由不重复对角组成的set。通地复用方法,让set template继承list,于是代码如下,
template<typename T> //将list应用于Set。是错误的做法
class Set:public list<T>{...};
上述做法错误的原因,public继承是is-a的关系,也就是Set的对象一定是List<T>对象,但我们知道,B有时表现出的的物性,不是D所需的,即D不是一种B。
于是转变策略,Set对象可根据一个list对象实现(is-implemented-in-terms-of)出来:
template<class T>
class Set {
public:
bool member(const T& item)const;
void insert(const T& item)const;
void remove(const T& item)const;
size_t size()const;
private:
list<T> rep;//用来表述Set的数据(复合的实现域用法)
};
template<class T>
bool Set<T>::member(const T& item)const {
return find(rep.begin(), rep.end()) != rep.end();
}
template<class T>
void Set<T>::insert(const T& item)const {
if (!member(item))
rep.push_back(item);
}
template<class T>
void Set<T>::remove(const T& iterm)const {
typename list<T>::iterator it = find(rep.begin(), rep.end(), item);//此处利用typename表明list<T>::iterator是一个嵌套从属名称
if (it != rep.end()) rep.erase(it);
}
template<class T>
size_t Set<T>::size() const {
return rep.size();
}
条款39 明智而审慎地使用private继承
统御private继承的首要规则:
i、如果class之间的继承关系是private,编译器不会自动将一个derived class对象转换为一个base class对象。
ii、由private base class继承而来的所有成员,在derived class都会变成private属性,纵使它们在base class中原本是protected或public属性。
需要注意:private继承意味只有实现部分被继承,接口部分应略去。private继承意味is-implemented-in-terms-of(根据某物实现出)。尽可能使用复合,必要时才使用private继承。
以下利用private继承及其改进版的代码举例,
class Timer {
public:
explicit Timer(int tickFrequency);
virtual void onTick()const;//定时器每滴答一次,此函数就被自动调用一次
};
以private形式继承Timer,
class Widget :private Timer {
private:
virtual void onTick()const;//查看Widget的数据...等等
};
对private继承形式,利用继承+复合版本进行改进,
//继承+复合代替private继承
class Widget {
private:
class WidgetTimer :public Timer {
public:
virtual void onTick()const;
...
};
WidgetTimer timer;//定义一个对象的前提,必须给定类的WidgetTimer的定义式,
}; //因为编译器在给其生成一个对象时,必须知道该类的大小。
另外一知识点,
C++裁定凡是独立(非附属)对象都必须有非零大小。由于前提是非附属哈,所以此约束不适用于derived class对象内的base class成分,以下均用代码验证,
class Empty {};//没有数据,所以其对象应该不使用任何内存
class HoldsAnInt {
private:
int x;
Empty e;
};
在32位的机器上,对上述使用sizeof(HoldsAnint)=8。
现实中的“empty”classes并不真的是empty。
class HoldsAnInt :private Empty {
private:
int x;
};
在32位的机器上,对上述使用sizeof(HoldsAnint)=sizeof(int)=4,即所谓的空白基类最优化(EBO)。
此部分具体细节分析见本人另一篇博客。
以上内容均来自Scott Meyers大师所著Effective C++ version3,如有错误地方,欢迎指正!相互学习,促进!!