EffectiveC++2eItem44
条款44: 说你想说的;理解你所说的 在本章关于 "继承和面向对象设计" 的简介中,我曾强调,理解不同的面向对象构件在C++中的含义十分重要。这和仅仅知道C++语言的规则有很大的不同。例如,C++规则说,如果类D从类B公有继承,从D的指针到B的指针就有一个标准转换;B的公有成员函数将被继承为D的公有成员函数,等等。这些规则都是正确的,但在将设计思想转化为C++的过程中,它们起不到任何作用。相反,你需要知道,公有继承意味着 "是一个",如果D从B公有继承,类型D的每一个对象也 "是一个" 类型B的对象。因...
EffectiveC++2eItem43
条款43: 明智地使用多继承 要看是谁来说,多继承(MI)要么被认为是神来之笔,要么被当成是魔鬼的造物。支持者宣扬说,它是对真实世界问题进行自然模型化所必需的;而批评者争论说,它太慢,难以实现,功能却不比单继承强大。更让人为难的是,面向对象编程语言领域在这个问题上至今仍存在分歧:C++,Eiffel和the Common LISP Object System (CLOS)提供了MI;Smalltalk,Objective C和Object Pascal没有提供;而Java只是提供有限的支持。可怜的程序员...
EffectiveC++2eItem41
条款41: 区分继承和模板 考虑下面两个设计问题: · 作为一位立志献身计算机科学的学生,你想设计一个类来表示对象的堆栈。这将需要多个不同的类,因为每个堆栈中的元素必须是同类的,即,它里面包含的必须只是同种类型的对象。例如,会有一个类来表示int的堆栈,第二个类来表示string的堆栈,第三个类来表示string的堆栈的堆栈,等等。你也许对设计一个最小的类接口(参见条款18)很感兴趣,所以会将对堆栈的操作限制在:创建堆栈,销毁堆栈,将对象压入堆栈,将对象弹出堆栈,以及检查堆栈是否为空。设计中,你不会借助...
EffectiveC++2eItem40
条款40: 通过分层来体现 "有一个" 或 "用...来实现" 使某个类的对象成为另一个类的数据成员,从而实现将一个类构筑在另一个类之上,这一过程称为 "分层"(Layering)。例如: class Address { ... }; // 某人居住之处 class PhoneNumber { ... }; class Person { public: ... private: string name; // 下层对象 Address address; // 同上 PhoneNumber voiceNu...
EffectiveC++2eItem39
条款39: 避免 "向下转换" 继承层次 在当今喧嚣的经济时代,关注一下我们的金融机构是个不错的主意。所以,看看下面这个有关银行帐户的协议类(Protocol class )(参见条款34): class Person { ... }; class BankAccount { public: BankAccount(const Person *primaryOwner, const Person *jointOwner); virtual ~BankAccount(); virtual void mak...
C++中用vectors改进内存的再分配
摘要:本文描述的是一种很常见的情况:当你在某个缓存中存储数据时,常常需要在运行时调整该缓存的大小,以便能容纳更多的数据。本文将讨论如何使用 STL 的 vector 进行内存的再分配。 这里描述的是一种很常见的情况:当你在某个缓存中存储数据时,常常需要在运行时调整该缓存的大小,以便能容纳更多的数据。传统的内存再分配技术非常繁琐,而且容易出错:在 C 语言中,一般都是每次在需要扩充缓存的时候调用 realloc()。在 C++ 中情况更糟,你甚至无法在函数中为 new 操作分配的数组重新申请内存。你不仅要...
C++中用函数模板实现和优化抽象操作
摘要:本文介绍函数模板的概念、用途以及如何创建函数模板和函数模板的使用方法...... 在创建完成抽象操作的函数时,如:拷贝,反转和排序,你必须定义多个版本以便能处理每一种数据类型。以 max() 函数为例,它返回两个参数中的较大者: double max(double first, double second); complex max(complex first, complex second); date max(date first, date second); //..该函数的其它版本 尽管这...
Heap Stack的区别
一、预备知识—程序的内存分配 一个由c/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的...
EffectiveC++2eItem22
条款22: 尽量用“传引用”而不用“传值” C语言中,什么都是通过传值来实现的,C++继承了这一传统并将它作为默认方式。除非明确指定,函数的形参总是通过“实参的拷贝”来初始化的,函数的调用者得到的也是函数返回值的拷贝。 正如我在本书的导言中所指出的,“通过值来传递一个对象”的具体含义是由这个对象的类的拷贝构造函数定义的。这使得传值成为一种非常昂贵的操作。例如,看下面这个(只是假想的)类的结构: class Person { public: Person(); // 为简化,省略参数 // ~Person...
EffectiveC++2eItem23
条款23: 必须返回一个对象时不要试图返回一个引用 据说爱因斯坦曾提出过这样的建议:尽可能地让事情简单,但不要过于简单。在C++语言中相似的说法应该是:尽可能地使程序高效,但不要过于高效。 一旦程序员抓住了“传值”在效率上的把柄(参见条款22),他们会变得十分极端,恨不得挖出每一个隐藏在程序中的传值操作。岂不知,在他们不懈地追求纯粹的“传引用”的过程中,他们会不可避免地犯另一个严重的错误:传递一个并不存在的对象的引用。这就不是好事了。 看一个表示有理数的类,其中包含一个友元函数,用于两个有理数相乘: c...
EffectiveC++2eItem24
条款24: 在函数重载和设定参数缺省值间慎重选择 会对函数重载和设定参数缺省值产生混淆的原因在于,它们都允许一个函数以多种方式被调用: void f(); // f被重载 void f(int x); f(); // 调用f() f(10); // 调用f(int) void g(int x = 0); // g 有一个 // 缺省参数值 g(); // 调用g(0) g(10); // 调用g(10) 那么,什么时候该用哪种方法呢? 答案取决于另外两个问题。第一,确实有那么一个值可以作为缺省吗?第二,...
EffectiveC++2eItem25
条款25: 避免对指针和数字类型重载 快速抢答:什么是“零”? 更明确地说,下面的代码会发生什么? void f(int x); void f(string *ps); f(0); // 调用f(int)还是f(string*)? 答案是,0是一个int——准确地说,一个字面上的整数常量——所以,“总是”f(int)被调用。这就是问题所在:因为不是所有的人总是希望它这样执行。这是C++世界中特有的一种情况:当人们认为某个调用应该具有多义性时,编译器却不这么干。 如果能想办法用符号名(比如,NULL表示n...