高质量C++/C编程指南-第10章-类的继承与组合(2)

10.2 组合
l 【规则10-2-1】若在逻辑上A是B的“一部分”(a part of),则不允许B从A派生,而是要用A和其它东西组合出B。

例如眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是头(Head)的一部分,所以类Head应该由类Eye、Nose、Mouth、Ear组合而成,不是派生而成。如示例10-2-1所示。

class Eye

{
public:

void Look(void);

};
class Nose

{
public:

void Smell(void);

};

class Mouth

{
public:

void Eat(void);

};
class Ear

{
public:

void Listen(void);

};

// 正确的设计,虽然代码冗长。

class Head

{

public:

void Look(void) { m_eye.Look(); }

void Smell(void) { m_nose.Smell(); }

void Eat(void) { m_mouth.Eat(); }

void Listen(void) { m_ear.Listen(); }

private:

Eye m_eye;

Nose m_nose;

Mouth m_mouth;

Ear m_ear;

};

示例10-2-1 Head由Eye、Nose、Mouth、Ear组合而成

如果允许Head从Eye、Nose、Mouth、Ear派生而成,那么Head将自动具有Look、 Smell、Eat、Listen这些功能。示例10-2-2十分简短并且运行正确,但是这种设计方法却是不对的。

// 功能正确并且代码简洁,但是设计方法不对。

class Head : public Eye, public Nose, public Mouth, public Ear

{

};

示例10-2-2 Head从Eye、Nose、Mouth、Ear派生而成

一只公鸡使劲地追打一只刚下了蛋的母鸡,你知道为什么吗?

因为母鸡下了鸭蛋。

很多程序员经不起“继承”的诱惑而犯下设计错误。“运行正确”的程序不见得是高质量的程序,此处就是一个例证。

时间: 2024-12-01 21:29:47

高质量C++/C编程指南-第10章-类的继承与组合(2)的相关文章

高质量C++/C编程指南-第10章-类的继承与组合(1)

对象(Object)是类(Class)的一个实例(Instance).如果将对象比作房子,那么类就是房子的设计图纸.所以面向对象设计的重点是类的设计,而不是对象的设计. 对于C++程序而言,设计孤立的类是比较容易的,难的是正确设计基类及其派生类.本章仅仅论述"继承"(Inheritance)和"组合"(Composition)的概念. 注意,当前面向对象技术的应用热点是COM和CORBA,这些内容超出了C++教材的范畴,请阅读COM和CORBA相关论著. 10.1

高质量C++/C编程指南-第9章-类的构造函数、析构函数与赋值函数(4)

类String的赋值函数比构造函数复杂得多,分四步实现: (1)第一步,检查自赋值.你可能会认为多此一举,难道有人会愚蠢到写出 a = a 这样的自赋值语句!的确不会.但是间接的自赋值仍有可能出现,例如 // 内容自赋值 b = a; - c = b; - a = c; // 地址自赋值 b = &a; - a = *b; 也许有人会说:"即使出现自赋值,我也可以不理睬,大不了化点时间让对象复制自己而已,反正不会出错!" 他真的说错了.看看第二步的delete,自杀后还能复制自

高质量C++/C编程指南-第11章-其它编程经验(2)

11.1.3 const成员函数 任何不会修改数据成员的函数都应该声明为const类型.如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性. 以下程序中,类stack的成员函数GetCount仅用于计数,从逻辑上讲GetCount应当为const函数.编译器将指出GetCount函数中的错误. class Stack { public: void Push(int elem); int Pop(void); int

高质量C++/C编程指南-第7章-内存管理(1)

欢迎进入内存这片雷区.伟大的Bill Gates 曾经失言: 640K ought to be enough for everybody - Bill Gates 1981 程序员们经常编写内存管理程序,往往提心吊胆.如果不想触雷,唯一的解决办法就是发现所有潜伏的地雷并且排除它们,躲是躲不了的.本章的内容比一般教科书的要深入得多,读者需细心阅读,做到真正地通晓内存管理. 7.1内存分配方式内存分配方式有三种: (1) 从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运行

高质量C++/C编程指南-第4章-表达式和基本语句

读者可能怀疑:连if.for.while.goto.switch这样简单的东西也要探讨编程风格,是不是小题大做? 我真的发觉很多程序员用隐含错误的方式写表达式和基本语句,我自己也犯过类似的错误. 表达式和语句都属于C++/C的短语结构语法.它们看似简单,但使用时隐患比较多. 本章归纳了正确使用表达式和语句的一些规则与建议. 4.1 运算符的优先级 C++/C语言的运算符有数十个,运算符的优先级与结合律如表4-1所示.注意一元运算符 + - * 的优先级高于对应的二元运算符. 优先级 运算符 结合

高质量C++/C编程指南-第8章-C++函数的高级特性(3)

8.2.2 令人迷惑的隐藏规则 本来仅仅区别重载与覆盖并不算困难,但是C++的隐藏规则使问题复杂性陡然增加.这里"隐藏"是指派生类的函数屏蔽了与其同名的基类函数,规则如下: (1)如果派生类的函数与基类的函数同名,但是参数不同.此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆). (2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字.此时,基类的函数被隐藏(注意别与覆盖混淆). 示例程序8-2-2(a)中: (1)函数De

高质量C++/C编程指南-第7章-内存管理(6)

7.10 malloc/free 的使用要点函数malloc的原型如下: void * malloc(size_t size); 用malloc申请一块长度为length的整数类型的内存,程序如下: int *p = (int *) malloc(sizeof(int) * length); 我们应当把注意力集中在两个要素上:"类型转换"和"sizeof". u malloc返回值的类型是void *,所以在调用malloc时要显式地进行类型转换,将void * 转

高质量C++/C编程指南-第7章-内存管理(5)

7.9 内存耗尽怎么办?如果在申请动态内存时找不到足够大的内存块,malloc和new将返回NULL指针,宣告内存申请失败.通常有三种方式处理"内存耗尽"问题. (1)判断指针是否为NULL,如果是则马上用return语句终止本函数.例如: void Func(void) { A *a = new A; if(a == NULL) { return; } - } (2)判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行.例如: void Func(void) { A

高质量C++/C编程指南-第7章-内存管理(2)

7.3.1 修改内容 示例7-3-1中,字符数组a的容量是6个字符,其内容为hello\0.a的内容可以改变,如a[0]= 'X'.指针p指向常量字符串"world"(位于静态存储区,内容为world\0),常量字符串的内容是不可以被修改的.从语法上看,编译器并不觉得语句p[0]= 'X'有什么不妥,但是该语句企图修改常量字符串的内容而导致运行错误. char a[] = "hello"; a[0] = 'X'; cout << a << e