Timothy

链接

《More Exceptional C++》------ ( 3 )

2010年9月03日 22:42 | Comments(1) | Category:Reading Notes | Tags:

 

条款29:auto_ptr
(1):auto_ptr不可用于数组情形.因为它删除内部指针时, 调用delete p,不会delete[] p.( 不要使用auto_ptr<T> t[n] )
(2):C++规定, 0长度数组合法, new T[0], 不会返回NULL, 返回数组地址,只是此数组没有任何元素可以被访问. (0长度数组依然不可以搭配auto_ptr)
 
条款30:智能指针的问题之一
(1):当类含有其他类指针时,注意拷贝问题,注意多次删除问题.
(2):用auto_ptr来包装这个指针,可以来内存分配和释放上有些许便利,但是对于拷贝和赋值依然存在问题.
 
条款31:智能指针问题之二
 
 
条款32:函数指针
(1):用代理类(重载向函数指针的类型转换 )实现 返回指向自己的函数指针.
	class CFuncPtrHelp;
typedef CFuncPtrHelp ( *FuncPtr ) ( void );

class CFuncPtrHelp
{
public:
	CFuncPtrHelp( FuncPtr p ) : m_pFunc( p ) {}
	operator FuncPtr() { return m_pFunc; }
private:
	FuncPtr m_pFunc;
};

CFuncPtrHelp Func()
{
	//other useful operation
	return Func;
}

int _tmain(int argc, _TCHAR* argv[])
{
	CFuncPtrHelp ptr = Func();
	ptr();

	system( "Pause" );
	return 0;
}

 

(2):typedef不能递归定义.
 
条款33:模拟嵌套函数
(1):嵌套类:定义在一个类内部的类,可以通过public, private等约束实现类的访问权限控制. 局部类:定义在函数内部的类,只对此函数可见,且不可用于模板参数.
(2):
 
条款34:预处理宏
(1):预处理宏的用处: a),预防头文件的多次包含 b),编译器预处理特性,_LINE_, _FILE_
c),条件编译( #ifdef __DEBUG ) d)去掉一些警告
 
条款35:宏定义
(1):宏常见错误: a),不要忘记给参数加上括号 b)不要忘记给展开式加上括号  c)注意多参数运算的问题  d)避免名字冲突,特别是和库中的函数等 
(2):其他宏需要注意到问题: a), 宏不可递归 b)宏没有地址 c)宏有碍调试
 
 
条款36:初始化
(1):变量初始化尽量使用 T t( u ), 而不是 T t = u.
 
条款37:前置声明
 
条款38:typedef
条款39:using和名字空间
条款40:名字空间使用

《More Exceptional C++》------ ( 2 )

2010年9月02日 23:12 | Comments(1) | Category:Reading Notes | Tags:

 

条款17:构造函数失败和异常
(1):对象的生命期, 从构造函数退出 到 进入析构函数.
(2):如果在对象构造函数就失败了,发生异常, 则说明此对象不曾存在过...
 
 
条款18:构造函数失败: 吸收异常?
(1):C++中,在构造或析构函数中使用 try catch是合法的,( 可以try 成员初始化列表), 但是函数也必须以抛出一个结束, 就算在catch中没写,但是在catch结束时,
也会由系统自动抛出默写一句:throw, 抛出异常结束.
(2):C++中, 只要基类或成员子对象构造失败,整个对象构造就会失败.
(3):在构造函数中使用try catch的意义就是转化 基类异常和子成员异常.( 不需要做所谓的析构处理,它们抛出异常就说明未曾产生 )
(4):析构函数不能抛出异常: 1, 析构就是清理工作,发生异常就是给你捕捉做清理工作,两者是矛盾的 2, 栈碾转机制,
    析构的异常,必须在析构的内部catch住.
    (这是C++标准里的一条)
(5):C++标准里的一条:在构造或者析构的try catch块中,引用对象的任何静态成员或者基类都将导致不可预测行为.
 
 
条款19:uncaught_exception()
(1):少用此函数, 而该写自己强壮的异常处理代码.
 
条款20:未管理指针存在的问题之一:参数求值
(1):形如 f( g( exp1 ), h( exp2 ) ), 表达式exp1, 和 exp2必然先于函数调用前求值, 也可能交叉进行,但是函数调用不可以交叉进行. 所以结果: 1就是exp1和exp2前求值,顺序不计;2就是exp1先求,再g(),然后去计算h(exp2),当然h( exp2 )在前也可以.
(2):多表达式参数函数, 求参时异常容易导致内存泄漏。
 
条款21:未管理指针存在的问题之二:auto_ptr
(1):把表达式求值隐藏到函数调用中,可以避免求值顺序的交叉或者异常导致的一些问题.
(2):把这样的表达式函数参数提出来,做成独立的显式资源分配,效果更好。
 
 
条款22:异常安全和类设计:拷贝赋值
(1):pimpl手法
(2):a),指针上的操作不会抛出异常 b)使用指针却带来内存和资源的问题,应该把他们管理起来(auto_ptr)
 
 
条款23:异常安全和类设计:继承
(1):松散的耦合可以提高安全性(包括异常安全),紧密的耦合降低'最大可能安全性'(包括异常安全). --- 包容 好于 私有继承
 
 
 
条款24:为什么使用多继承
(1):避免多继承自多个 非纯虚基类.  ( 多继承一般适用于 多接口继承)
(2):使用场合:a) 继承多个已存在的库 b) 多接口继承(COM) c)易于多态使用, 子类可以方便替代多个使用基类的地方.
 
 
条款25:如何模拟多继承
 
条款26:多继承和连体双婴问题
(1):多继承自CBase_a,CBase_b,若在两个基类中有同名虚函数 virtual ... FuncV( ... ) [ = 0 ], 要实现子类重写而具有两个功能,方法:
分别从CBase_a, CBase_b派生出代理类,用不同函数调用这个FuncV,子类再从此二者多继承,改写这两个不同的函数实现(子类切不可修改FuncV).
 
条款27:纯虚函数
(1): 纯虚函数也可以出现定义函数体: a),纯虚析构函数  b)提供缺省行为,明确使用 c)提供部分行为 d)
(2): 基类的析构函数,要么应该是虚public函数,要么应该是非虚protected函数(确保不通过基类指针删除子类).
 
 
条款28:受控的多态
(1): 如何让Func1在接受CBase&参数时,可以多态的使用CDerived&, 但是禁止Func2这样:
      声明CDerived私有继承自CBase, 然后Func1是它的 友元.
 
 
 

《More Exceptional C++》------ ( 1 )

2010年7月24日 06:15 | Comments(1) | Category:Reading Notes | Tags:

 

条款一:
 
(1):std::cin和std::cout的类型是 std::istream和std::ostream, 分别是std::basic_istream<char, std::char_traits<char> > 和 std::basic_ostream<char, std::char_traits<char>的typedef。
 
(2):代码要注意可阅读性,避免简洁但却不宜读的代码.
 
(3):提高代码的可扩充性. 保证封装性,关系相分离.
 
 
 
条款二:
 
(1):std::remove(), 是将要删除的元素移到容器(或区间)的末尾,其他元素相应前移,容器(区间)的长度不会改变.  移到末尾的元素,可能保持原值,也可能不会.
 
      这样的原因: remove()是作用在迭代器上的, "从一个容器中删除迭代器所指的元素"这样的迭代器操作不存在。
 
      删除容器的元素应该使用容器的erase方法.
 
 
 
条款三:
 
(1):STL的算法使用仿函数或者函数对象时, 是传值使用。
 
 
 
条款四:
 
(1):trait类: 一个封装了一组类型和函数的类,以使模板类和模板函数可以操纵 实例化类型 的对象.( 实例化类型:即模板实例化时使用的类型)
 
      trait的封装,主要是指把这些数据和接口放在一起,不是指把这些隐藏在某些外壳中,所以trait类一般都是public实现或是一个struct.
 
(2):测试一个 模板类型类 中是否有一个 T* Clone() const的函数
 
     好的实现:
 
 
 
template<typename T>
class CA
{
public:
    ...
    ~CA()
    {
         assert( IsTValid() );
    }
private:
    BOOL IsTValid()
    {
        T* (T::*test) () const = &T::Clone();
        test = NULL;   //避免未使用的变量警告
    }
 
}
 
 
条款5:typename的使用
 
条款6:容器
 
(1):关于取得容器类元素的指针(或者迭代器),要注意迭代器失效的问题.
 
(2):std中,所有的迭代器的*运算,都得到一个容器包含对象的引用
 
(3):vector<bool>的特殊性,伪容器, 在内部存储时,bool类型会优化,将会把8个bool合成一个BYTE存储,这样实际上vector中就是8个元素存储在一起的,
这样,要实现它必须写代理类,在返回标准迭代器和元素时.(这样的应用应该使用 bitset )
 
条款7:vector 和 deque
 
(1):deque是分段连续存储的,他总是在插入式与分配以后的内存, 而vector是在内存满时在分配.
而deque在内存清理时,也比vector费劲.
(2):vs vector:deque提供了push_front, 但是减少了 reserve() 和 capacity() 函数.
(3):如何清除多余的vector内存, vector<T>( vec ).swap( vec )....vec多余的分配内存即清空和释放了(vec.capacity() 等于或只略大于 vec.size() )
 同理, vector<T>().swap( vec ),...把vec的内容和内存都清理( vec.capacity() = 0现在 ) ,对deque容器是同样的.
 
条款8:set 和 map
(1):map<key, value>的迭起器返回的是 pair<const key, value>, 若采用const_cast的方法强制修改key,不会引起map数的重新旋转,会让map失去存储意义.
应该采用 先erase, 再insert的方式, set同理.
 
条款9:相同的代码吗?
(1):避免使用宏, 运算符重载要保持运算符的普通语义.
(2):f( a++ ), 执行顺序是: 先a++, 再用a的旧值掉用 f(),和代码:f( a ); a++; 的区别是: 在f()发生异常时, 第一种++已发生, 第二种还没有
在比如 f = list<T>::erase(), a = iterator中, 第一个合法, 第二个却不合法了,因为erase会引起迭代器失效....特别要注意函数的副作用,安排程序语序.
 
条款10:模板特殊化和重载
(1):非模板函数 优先于 模板函数匹配(不发生隐式转换), 除非是显式的调用模板函数.
 
 
条款11:略
 
条款12:内联
 
条款13:缓式优化之一 - copy on write
(1) 如题
(2) 关于容器内存不够时,扩展内存的策略:
    a),精确增长,   内存分配 O(n), 字符copy动作 O(n), 空间浪费 0
    b),固定长度增加,        O(n),              O(n),         O(1)
    c),指数增加(每次增加0.5当前size)    O(logn) O(1)     O(n)
条款14:缓式优化值二 - 实现copy on write
 
条款15:缓式优化值三 - 完善copy on write
条款16: 多线程环境下的copy on write(
( 13 -16 一个string类的优化 )

From Com to Com[转:侯捷作品]

2010年7月09日 01:35 | Comments(1) | Category:Learning Plan | Tags:

 摘要:

 
本文简介 C++ Object Model 和 Component Object Model 的基本概念,并引介四本书籍:
 
1. Inside The C++ Object Model 
 
2. Essential COM 
 
3. Inside COM 
 
4. Understanding ActiveX and OLE -- A Guide for Developers & Managers 
 
将近 8 年的时间,我把自己放逐在 Windows 领域里,纵情学习与研究。我应该算是幸运的一群人之一,在 PC 软体工业起飞的年代,一步步地抓住技术提升上来。我的那一票工程师朋友们,因为在职的缘故,整天但求把专案完工了结尚不可得,实在难有好好沉淀心得、充电进修的时光。有一位朋友苦於整体技术的盘根错节,就曾感慨地说,如果能够循序渐进地学习,该有多好!还有一位读者写信来对我说:沉浸在书籍里的学习滋味,真是曼妙。
 
可惜,世界不等待我们。我们也只好采取「跳岛策略」。
 
在这麽多年的技术研究中,我感觉自己像在玩拼图游戏。以程式语言始,以作业系统终,其间包括 C、C++、Java、SDK、MFC、VxD、Win32 OS、Multithreading、C++ STL、Internet Protocol...。我望着手上这块拼图,感觉外界的一切变化,差不多可以在我的手心里头掌握了。不管再有新发展出的什麽物件导向语言、什麽 application framework、或是什麽新的driver model、新的 protocol,我都觉得因为掌握了基本知识,而得以触类旁通,举一隅以三隅反。就算是换一个作业系统,我都觉得由於我对 processes、threads、modules、address space、executable file format 的认识,使我可以轻而易举地易地而行。是的,我感觉一种前所未有的安定。
 
然後我审视我的技术蓝图,发现还有一块空白没有填上。那其实是多年来的梦魇叫做 OLE 的东西。那东西从面世到今天,不断地成长,已经和大树一样了。它不但分割出 COM 底层架构,又在 Internet 当道之际,加入更多特质,幻化为 ActiveX。
 
过去我也曾经在 OLE 身上下了很大的功夫,甚至也得到一些至今看来仍觉不错的成果。我常常在检阅以前读过的 OLE 书籍时,看到密密麻麻的心得眉批以及用 Word 和 Powerpoint 做出来的精美心得,还有已经完成的部份书稿,惊讶地发现自己原来也已经钻研到那样的一个程度。然而也总是在片刻的陶醉之後,心里深处浮现一个小小的声音问自己:为什麽现在全都忘了?
 
 
●Know How 与 Know Why
 
就好像骨钙的补充一样,重要的不在吸收多少,而在留下多少。 我一再遗忘曾经掌握的 COM/OLE 技术,原因恐怕是,我并没有扎扎实实地把相关知识奠定好基础。我也许学到了不少 how,但我不知道 why。对於原理的不了解,使我没有办法留住我所吸收的养份。
 
这与我向来的研究精神悖离。但寻寻觅觅好久,我并没有找到理想的文献,能够把 OLE(其实应该说是 COM)基本原理交待清楚。我甚至不知道是否真的有那样的文章或书籍。其实我只需要最根源的那一部份。悟了最根源的部份,我就有能力度化自己了。
 
在很偶然的情况下,我凑齐了两本书,才终於整合出潜意识寻寻觅觅而不自知的东西。
 
说是偶然,何尝不是日积月累的周边知识所导至的因缘俱足?
 
 
●两本好书
 
我所说的这两本书是 Inside The C++ Object Model 和 Essential COM。请注意,它们都在讲 object model,一本针对 C++,一本针对 components(软体元件)。
 
早些年我曾经对 C++ objects 在记忆体中的布局有很大的兴趣,也整理出虚拟机制底层建设的一些心得(当时我并不知道这个知识领域被称为 C++ Object Model)。所以当我翻看 Inside The C++ Object Model 三两页,直觉告诉我,这正是我一直希望拥有的一本书。事实上它已出版两年了,我真是後知後觉!
 
拿到上一本书的同一时间,我也拿到了最新出版的Essential COM。此书前两章介绍 COM 的 "why",正是我所亟需。我感觉讨论「为什麽需要 COM」这一主题所需要的技术背景之中,好多正是 C++ Object Model 所涵盖的范围。而且,Essential COM 的作者 Don Box 竟然也在书中第 16 页大力向读者推荐 Inside The C++ Object Model 一书。这不是因缘俱足吗?我自己的发掘与 Don Box 的推荐,两相映和,快乐难以形容。
 
这一次,就让我为大家铺一条从 C++ Object Model 通往Component Object Model 的大道。
 
 
●倒叙
 
恐怕我得用倒叙的方式,才能让你明了(并且认同),为什麽学习标榜着「与程式语言无关」的 COM,却需要先有 C++ 语言的某种非常深层结构的(属於编译器层次的)基础。
 
认真地想想这个问题:我的硬碟中同时存在有MFC20.DLL、MFC40.DLL、MFC41.DLL、MFC42.DLL、MFC50.DLL...,为什麽会这样?因为版本更新时,我懒得去整理硬碟。现在我想到了,看到了,但是我敢动手砍掉它们,只保留最新版本吗?我不敢,因为我不确定我的哪一个应用软体使用哪一版 DLLs。我宁愿多花一点磁碟空间来放置所有历史陈迹 -- 反正硬碟也便宜!
 
一个回避的方法就是,乾脆软体开发人员都不要使用DLLs,都用静态联结(static linking)算了!这是「因噎废食」的最佳写照。有这种念头,我就走入历史的反动了。要知道,DLLs 是一种可重复使用的模组,不必因为 client(呼叫端)的改变而重新编译联结。反方向说,DLL 若更新也不必与 client 重新联结(如果介面未改的话)。不使用 DLLs,每一个应用软体都会变成超级大胖子,这是问题之一。不使用 DLLs,应用软体的开发过程会遭受重大阻碍,执行时严重浪费资源(尤其是记忆体),这是问题之二。
 
所以软体工程师们决定继续使用 DLLs。假设我出厂一批DLL 1.0,一年後决定修改其中的某个 class,为它加上一笔 private data。当与 1.0 同档名的 DLL 2.0 被安装到使用者硬碟中,旧版 DLL 被覆盖掉。对於新的应用程式(DLL 2.0 的呼叫者),这当然没问题。但当那些原本呼叫 DLL 1.0 的应用程式被执行起来,系统可能当掉!
 
为什麽?C++ 不是支援资料封装吗?新加的 DLL 资料不是 private 吗?client 永远不可能直接存取那笔资料呀!话虽不错,但 C++ 向来只靠 private 和 protected 两个保留字提供语意上的封装,并没有提供二进位层次(binary level)的封装。C++ 编译器必须对物件的实体布局有完全的掌握(二进位上的掌握),才有办法制造出一个物件实体;上述例子新加入的一笔 private 资料改变了物件实体的记忆体布局,旧的 client 呼叫新的 DLL,呵呵,不当掉是运气!
 
解决方法就是目前习用的「以不同的 DLL 档名代表不同的版本」,像 Microsoft 对其 Visual C++、Visual Basic 的所做所为一样。这是一种锯箭法,回避了问题,但没有解决问题。
 
 
●Binary Level
 
真正要解决这个问题,必须让 DLL 这一端的任何改变,都不必引起 client 端的任何变化。真正的封装,必须把「物件看起来像什麽」和「物件实际上如何工作」彻底分隔开来。这样的原则在一般的 C++ object 二进位层级(bineary level)中无法施行(为什麽?说来话长,这属於 C++ Object Model 的范畴)。但如果我们把原本的 C++ class 改装为一个 interface class 和一个 implementation class 的组合,就可以办到。Implementation class 以 DLL 的形式出现,而 interface class 成为 implementation class 的一个基础类别,其间没有任何资料(data members),只有欲开放之 implementation class methods 的各个函式宣告。你猜对了,这些宣告统统被设计为纯虚拟函式。纯虚拟函式并不是唯一一种作法,却是最佳作法。
 
Interface class 负责和 client 联结,形成 client 和object DLL(implementation class)之间的一道防火巷,完成所谓的二进位封装!
 
除了软体版本冲突,另一个问题也有待解决。由於各家 C++ 厂商对於 mangling(函式名称改装)的作法不一,没有标准,在 C++ 平台A中所制作的 classes DLL,不保证能在 C++ 平台B中被使用。上述把 interface 和implementation 分开的作法,可以解决所谓编译器不相容的问题。此外,对於 runtime polymorphism 也有帮助,对於 object extensibility 也有帮助。
 
 
●所谓 Components
 
受到二进位防火巷保护,并且免除 name mangling 困扰的objects,就是所谓的 components。它不自限於使用哪一种C++ 编译器来开发,甚至不自限於使用哪一种程式语言。事实上,Component Object Model 是一个规格,只不过这个规格最贴近 C++ 虚拟函式的实作方式罢了。同类型的规格还有 System Object Model(SOM)和 Common Object Request Broker Architecture(CORBA)。
 
我想你已经看到了,要了解 COM,唯有先了解「为什麽需要 COM」。要了解「为什麽需要 COM」,又必须先了解「C++(或目前的任何语言)为什麽做不到」。而要了解「C++ 为什麽做不到」,就必须了解「C++ 物件模型」是怎麽一回事。
 
■Inside the C++ Object Model 
 
作者:Stanley B. Lippman 
出版公司:Addison Wesley 
出版日期:1996 页数:7 章,280 页
售价:书上没有标示定价(本书无磁片)
1. Object Lessons 
2. The Semantics of Constructors 
3. The Semantics of Data 
4. The Semantics of Function 
5. Semantics of Construction, Destruction, and Copy 
6. Runtime Semantics 
7. On the Cusp of the Object Model 
 
 
 
在 C++ 成山似海的书籍堆中,这一本并不是婴幼儿奶粉,也不是较大婴儿奶粉,它是成人专用的低脂高钙特殊奶粉。
 
我曾经在几次 C++ 课程或 MFC 课程中,为学员补充一些有关於 C++ objects 在记忆体中的长像与栏位布局的资料、以及虚拟函式的实作技术、类别继承後的物件内容、隐藏的 this 指标...等等议题,受到很大的欢迎。这些反应,都不脱我的预料:工程师渴望知道底层的东西。
 
很多人疑惑我为什麽知道这些底层知识(早些年还有人认为我大概拥有Microsoft 或其他什麽公司的後门管道 -- 这是我的朋友在某个技术研讨会场上听到讲师当众说的)。这些知识一部份是从各式各样的技术文件或某一本书或某一篇文章断简残篇地获得,一部份是自己实际写点小程式做实验而得。
 
我以杂学的方式对 C++ 物件模型所整理出来的轮廓,说起来还粗浅得很。1997 年底不经意地发现了这本Inside The C++ Object Model,这才把自己的缺憾完全弭补过来。
 
这本外表毫不起眼的小书,又薄又窄,比一般原文书的大号尺码小得多。乾乾瘦瘦,才 280 页。「俗艳的」封面设计再加上微黄的纸张,原本引不起我多大的兴趣。但是作者大名使我在上面多瞧了两眼(Lippman 着有 C++ Primer,与Bjarne Stroustrup 的 The C++ Programming Language 齐名,目前已是第三版)。这一瞧不得了,开始有点「漫卷诗书喜欲狂」的感觉。後来在 Essential COM 身上看到 Don Box 推荐这本书:"...Consult Stan Lippman's most excellent Inside the C++ Object Model for a great discussion of both techniqies." 我同意 Don Box 所言,这本书的确 "most excellect"。
 
本书返朴归真,不仅外貌如此,内容也如此。所谓 C++ 物件模型,闻之令人丧胆,彷佛和什麽 OOA/OOD 有关。Lippman 在其前言中谈到,C++ 物件模型有两个切入点:
 
1. 对於语言所提供之物件导向程式设计的直接支援 
The direct support for object-oriented programming provided within the language
 
2. 用以实作出上述支援的底层机制 
The underlying mechanisms by which this support is implemented
 
本书谈的是第二个切入点。如果你对於物件和资料的组成有浓烈兴趣,第一章的 Object 和第三章的 Data 一定可以满足你。不管类别继承方式是 virtual、single 或 multiple,资料属性是 static、global 或 local,这两章都有所讨论,并在关键处以图片表示。这部份如果没有以图片来表示,就不值得我的推荐了,毕竟要从文字叙述中构想出资料栏位的布局,委实太过辛苦。本书示意图虽然从美学的角度上可用「丑陋」来形容,不过尚足够清楚表现出作者的意念,於功能上我想也够了。
 
如果你对几个基本的成员函式如 default constructor、copy constructor、virtual destructor,以及对於各类型函式如 global、virtual、static 的底层运作感兴趣,第二章的 Constructor、第四章的 Function、以及第五章的「建构、解构和拷贝」绝对可以满足你。第六章续谈函式,并谈到 new 和 delete 两个运算子。第七章谈 C++ 的三大扩充:template、Exception、runtime type identification(RTTI)。
 
针对各个主题,Lippman 都涵盖设计的由来、版本的演进、以及标准和实务两种作法,并对某些主题提出不同平台(platform)的效率测试结果。这本书基本上是以较学术的方式来撰写,所以在数据的处理以及叁考资料的整理方面,都很仔细。
 
说到学术,应该就是严谨的代名词。但是这本书的笔误大约有 100 个。我不敢乱说,侯俊杰先生正在译这本书,他列了一份原文勘误表出来,密密麻麻。其中的 C++ 明显语法错误,比较无伤大雅,杀伤力也有限,然而像程式码符号名称与内文叙述不一致,或是物件布局的图解与程式码的宣告不一致,都会严重误导读者的心力。以此观之,说实在,本书虽然到处都有牛肉,但某些部位的烹调水准,实在有损 Lippman 的大师声誉。
 
280 页的厚度,以现今动辄千儿八百页的满汉大餐来说,宛如一盘小菜。但是呛辣带劲儿,不是练家子不易下咽。这不是一本 C++ 初学者看的书,对於没有专案压力、没有考试压力、有治学精神的人,侯捷强力推荐。
 
■Essential COM 
 
作者:Don Box
出版公司:Addison Wesley
出版日期:1998
页数:7 章,440 页
售价:US$ 34.95(无磁片)
 
1. COM as a Better C++
2. Interfaces
3. Classes
4. Objects
5. Apartments
6. Applications
7. Miscellanea
A. The Evolution of Objects
B. Selected Code Fragements
 
 
 
 
所有在 Microsoft 平台上讨生活的软体工程师,没有人不知道 COM/OLE/ActiveX 的重要性,没有人不想了解并进而熟练这项技术。坊间不是没有好书,例如 Inside OLE (Fraig Brockschmidt/Microsoft Press/1995)就几乎是每一本相关着作都要列入 Bibliography 或 Reference 的经典。但是该书很像一本「有许多字的天书」,侯捷也在上面爬得很辛苦。
 
究其因,早期大家对於 Object Model 的程度不够,学习起 COM/OLE 来很吃力。慢慢大家书看得多了,在实际开发工作上运用 C++/OO,也有一些经验了,再来学习 COM/OLE,比较能够渐入佳境。
 
另一个原因是,能够把 COM 来龙去脉说得一清二楚的好书,过去绝少。我不能够说没有这样的书,也许是自己孤漏寡闻。众里寻它千百度,现在它来了,就是这本 Essential COM。
 
 
●关於 Don Box
 
作者 Don Box 在 COM/OLE 这一领域可说是赫赫有名。他在 Microsoft Systems Journal(MSJ)主持一个 ActiveX/COM 问答专栏,早就显露了卓越的实力。让我节录本书两篇序文对於 Don Box 的赞誉给你瞧瞧。你说得对,序文哪有不吹捧作者的,欢乐一家亲嘛,不过用词遣字之间你还是可以感受胡乱吹捧和诚恳推荐之间的大不同:
 
第一篇序文:Charlie Kindel COM guy, Microsoft Corporation
 
如果有一本英文书谈的是 COM、DCOM、OLE 或 ActiveX,而我也读过它,你几乎一定会发现我的名字被列在技术检阅那一栏,做为信誉保证。我自己也写了许多这方面的技术文章,我同时也是 COM Specification 的主要编辑。我给过无数的 COM 简报,不管对象是技术人员或非技术人员。很明显,我花了许多时间和精力,尝试找出 COM 的最佳说明方法。
当我读过这本书的最後草稿,我明白我所有的努力都白费了。关於 COM,没有任何人能阐释得比 Don Box 更棒!
 
 
第二篇序文:Grady Booch
 
一本书的好东西如果太多,就值得说两次。这是为什麽Don 这本书有两篇序的原因。
 
如果你正在 Windows 95 或 NT 上建立系统,你不可能远离 COM 的范畴。如果你想要 (1) 了解台面下发生什麽事 (2) 利用 COM 的强大威力,Don 这本书很适合你。
 
我特别喜欢这本书的一点是,Don 在说明 COM 时的组织方式。如果阅读本书时你对 COM 一无所知,你会被一个清晰简单的 COM 观念模式导引,使你了解问题之所在,以及 COM 之所以获得其结构和行为的原因。如果你是一位有经验的 COM 开发者,那麽你一定会特别赏识 Don 以 COM 解决一些常见问题时,提出的各种角度的讨论。
 
注意,第二篇序的执笔人是 Grady Booch,享誉全球的物件导向大师人物。
 
 
●为什麽需要 COM
 
COM 的全名是 Component Object Model,是微软为了解决软体版本冲突、物件跨平台冲突...等等问题而设计并主推的一种物件模型。它是一份规格,而 C++ 的抽象类别与虚拟函式的实作方式最贴近这份规格。
 
我相信,在学习如何运用 COM/OLE 技术之始,所有人的疑问都相同:为什麽要有 COM?是呀,说到物件导向技术,C++ 不是提供了物件封装、继承、多型的特性了吗?COM 所为何来?
 
本书第一章可以让你清楚 COM 的设计动机,包括interface、reference count、CreateObject...。只要通晓 C++ 语言,在形式上,看这一章就没什麽困难。如果你有 C++ Object Model 的基础,第一章更可以给你醍醐灌顶的感觉。
 
第二章介绍 COM interfaces。作者在前一章步步经营演化而来的一个 virtual base class 此时摇身一变成为功能对等的 COM IUnknown interface。这一章介绍了IDL(一种 interface 描述语言)、MIDL(微软的一套IDL 编译器)、GUID(保证亿万年不重复的一个 128-bit 数字)、HRESULT(任何平台通用的一种回返值型态)、IUnknown(所有 COM interfaces 的基础)。由於许多COM programmer 不能够恰到好处地呼叫 IUnknown 的AddRef 和 Release 两函式,Don Box 特别整理出好青年十大守则,并提供一个实例,告诉读者什麽时候适用哪一条守则。很贴心!
 
第三章开始,本书往深涩的方向走去。这时候我建议你先跳看另一本书:Inside COM,然後再回头接续看後面的章节。
 
本书用词比较学术化(我的意思是艰涩)。这会使不习惯的读者在攻击发起时进度受阻。阅读时请特别留心前两章,它们是学习 COM 的关键。这两章内文有系统地围绕一个例子打转,循序渐进。可惜本书并没有分阶段地附上各级程式码(从抽象基础类别的设计开始、然後是 interface 与 implementation 的分离、DLL 的加入、reference counting 的设计、QueryInterface 的设计...)。我强烈建议你要有实作的欲望和精神,依书中的指示一步一步实地靠近 COM。这当中你可能会吃点苦头,因为书中没有给你所有的资讯(例如 console DLL 的设计..,基本上那算是你的必要基础)。也许你会卡在某一个小小关卡上进不得、退不甘。但无论如何,你必须尝试实作,才会有最踏实的收获。这是我的真人实证。
 
本书相关程式码可以从 Don Box 的网站中取得,不过并不包含上面所说的 COM 骨干程式的各个发展阶段实作码。
 
■Inside COM 
 
作者:Dale Rogerson
出版公司:Microsoft Press
出版日期:1996
页数:12 章,376 页
售价:US$ 34.99(含 CD 一片)
 
1. Components
2. The Interfaces
3. QueryInterface
4. Reference Counting
5. Dynamic Linking
6. HRESULTs, GUIDs, the Registry, and Other Details
7. The Class Factory
8. Component Reuse : Containment and Aggregation
9. Making It Easier
10. Servers in EXEs
11. Dispatch Interfaces and Automation
12. Multiple Threads
13. Putting It All Together
 
 
 
 
这本书文字浅显,义理精确,前进速度适中,每一个阶段附有一个完整的程式。只可惜起头不够好,没有像Essential COM 一样地从 COM 起源开始分析。万一一开始的基础没有打好,对整个 COM 的基本精神尚自摇摇晃晃,後面的推进速度恐怕就无法打到五档了。
 
我的经验是,当我看完 Essential COM 前两章,并在其第三章匍匐前进,磨得双膝微颤,舌头发苦时,回过头来看这本 Inside COM,一个晚上刷刷刷看掉了 130 页(前六章),而且掌握得相当不错。
 
本书每一章每一节的前後转承文字说明接续得非常不错,每一个句子的语意也很清晰(不会用一大串一长串的子句连接词),让我们一路读来顺畅快意,不需要多花心思在其文章架构,可以把全付精神用来对付 COM 技术。
 
拿 Inside COM 和 Essential COM 搭配着看,可得最大效益。
 
■Understanding ActiveX and OLE -- A Guide for Developers & Managers
 
作者:David Chappell
出版公司:Microsoft Press
出版日期:1996
页数:11 章,328 页
售价:US$ 22.95(无碟片)
 
1. Introducting ActiveX and OLE
2. The Component Object Model
3. Marshaling and Type Information
4. Automation
5. Persistence
6. Monikers
7. Uniform Data Transfer and Connectable Objects
8. OLE Compound Documents
9. ActiveX Controls
10. Distributed COM
11. ActiveX, the Internet, and the World Wide Web
 
 
 
 
这本书的副标明白告诉了我们,它是一本可以给经理人看的书。既然是为经理人而写,书中就不会有太多的 code,反倒是抽象观念的文字说明比较多。
 
我从来不相信「抽象的文字说明」能够让不具「具象技术基础」的人看得懂。我敢把话说死,是的,上面这句话对99.99999% 的工业界人士适用。所以,这本书其实只适合给受过 COM/OLE/ActiveX 训练的工程师看。你已经会了,你才看得懂它。
 
会的人才看得懂,不会的人都看不懂,那这本书不是一点用处也没有了吗?不,它的内容整理得不错,并附予一些设计良好的图,可以使我们的观念更清晰,甚至修正我们可能存在的一些模糊的、似是而非的盲点。书侧留白处很细心地标示出段落文字的重点,有点领袖嘉言录的味道。双色印刷,令人赏心悦目。
 
--- the end ---

Genericity/STL 大系[转:侯捷作品]

2010年7月09日 01:34 | Comments(1) | Category:Learning Plan | Tags:

 如果有一项技术,可以让你的程式码处理各种不同的资料型别,甚至是目前未知的资料型别,你喜欢吗?

 

我会欣喜若狂。

 

基本上这就是「可复用性(reusibility)」的表现。当我们有新的资料型态产生,而过去完成的码完全无需修改即可沿用,不正是一种完美的「可复用性」吗?

 

物件导向技术中的多型(polymorphism),以及泛型技术中的泛型(genericity)都可以达到这个目标。它们的字义,也明白标示出其特色。对大多数人而言,polymorphism(多型技术)早已如雷灌耳,genericity(泛型技术)则稍感陌生。这是一个你有必要尽快进入的重要领域。

 

 

 

●勤前教育

 

数年前我第一次接触泛型程式设计(generic programming)与 STL(Standard Template Library)的时候,就深深被它吸引。虽然那时候我还不怎麽了解 STL 里头一大堆的术语像是 container、iterator、adaptor、function object、allocator┅。甚至连泛型技术深度依赖的基本技法 C++ template,当时的我都还只一知半解,但光只「泛型」这两个字就够把我吸引到那个世界里面了。

 

但愿我这麽说不至於误导你把泛型程式设计和 STL 划上等号。泛型概念滥觞於 Doug McIlroy 於 1968 年发表的一篇着名论文 "Mass Produced Software Components",那篇论文提出了 "reusable components"(可复用软体组件,又称为软体积木或软体 IC)的愿景。过去数十年中,泛型技术仍属於研究单位中的骄客,实作产品付之阙如。直到 C++ 不断加强 template 机制,并将 Alexander Stepanov 创作的 STL 纳入标准,泛型技术才终於在标准资料结构和标准演算法领域中有一套可被大众运用的实作品出现,向现实跨一大步。

 

让我们先复习一下。下面是多型的标准形式:

 

void func(Shape* ps)  // Shape 是某个 class 继承体系的基础类别

{

    // ...

    ps->draw();  // draw() 是个虚拟函式 (virtual function)

}

 

func() 的呼叫者可以自由传入 Shape 继承体系下任何一个 Shape 衍生类别的物件指标,func() 函式所唤起的将是实际传入之物件(指标)所对应的那个 draw() 虚拟函式。这种写法所带来的好处是,即使将来  Shape 继承体系衍生出前所未见的子型别,只要该子型别本身提供了 draw() 虚拟函式,上面这个 func() 就完全不必更改,可继续使用。

 

那麽,泛型又是什麽呢?简单地说,这是一种「将资料型别叁数化」的思维模式。C++ 的 template 机制就是泛型技术的一个具体载具。在 C++ 中,不论 functions 或是 classes,皆可将其中所需要的资料型别以一个保留器(placeholder)代表,这个保留器亦即所谓的 template 叁数。例如 function template:

 

template<typename T1, typename T2)

void func(T1 param1, T2 param2) { /* ... */ }

 

或是 class template:

 

template<typename T1, typename T2)

class A { /* ... */ }

 

从此,一旦程式中使用上述的 func() 或 class A,例如:

 

func(5, 2.3);

A<int, double> a;

 

编译器即根据 function template 的函式引数(上例的 5 和 2.3),或根据被我们明白标示出来的 class template 引数(上例的  int  和 double),自动推导出一份 function 实体或 class 实体。这个所谓的具现化动作(instantiation)在编译期就完成,不会增加执行期的成本。关於 template 的详细语法与性能,请叁考任何一本完备的 C++ 百科型书籍。

 

以上这种「将资料型别叁数化,再由编译器视使用当时的情况,为我们完成实体具现化」的概念,即是泛型的实际展现。template 是 C++ 语言中支援泛型概念的一个工具,而 STL 则是泛型概念的一套实作品。从学理上来说,STL 其实是一套严谨的 "concepts" 分类学。这里所谓的 concepts 有其严谨定义,意指「对某种型别的某些条件需求」。满足这些条件之型别,称为该 concept 的一个 model。举个例子,如果我们能够复制型别为 T 之物件,并可以将数值指派给 T 型别的变数身上,那麽型别 T 便符合 Assignable 这一 concept,而 T 便是 Assignable 的一个 model。STL 的六大组件 containers, algorithms, iterators, function objects, allocators, adaptors, 全都是 concepts,实作品如 vector, list, sort(), swap() 等等 templates, ... 全都是 models。

 

这样的学理概念,对大部份人勿宁是不可承受之重。大部份人只着眼 STL 的实用性,因为 STL 非常好用,弹性非常大,执行效率也很理想,可大幅提升软体开发效率。从实作的角度来看,以各种方式完成,符合 STL concepts 需求之各种 C++ classes 和 C++ functions,就是大家一般印象中的 STL,它们实际存在於各个相应的含入档中,如 <vector>,<functional>, <algorithms>.

 

 

●剖析 STL

 

任何学习,如果直接从抽象思维开始,对大部份人是一件困难的工作。通常我们需要一个具体可操作的东西,慢慢再由具象操作转为抽象思考。

 

那麽,先学会使用 STL,应该是学习泛型思维的最好途径。事实上,自从 STL 以及整个 C++ 标准程式库定案之後,很多专家,包括 Bjarne Stroustrup,都认为 C++ 程式语言的教学,首先应从如何使用标准程式库(含 STL)开始。

 

我当然无法在这篇文章中告诉你 STL 乃至整个标准程式库的用法。但是我可以给你一些概念,让你知道 STL 的架构。

 

STL 是一个完全以 template 技术完成的程式库。它构成了 C++ 标准程式库的绝大部份骨干 ─ 粗略估计应该占 80% 以上的面积。STL 有六大组件(components):

 

1 containers(容器),各种基本资料结构如 vector, list, deque, set, map┅,共约 11 种。其中有些亦被归类为 adaptors。

 

2 algorithms(演算法),各种基本演算法如 sort, search, copy, erase┅,共约 70 个。

 

3 iterators(迭代器):应用於容器与演算法身上的一种泛型指标,扮演两者间的胶着剂。[Gamma95] 对於 iterator 这种设计样式(design pattern)的定义是:提供一种方法,俾得依序巡访某个聚合物件(容器)所含的各个元素,而又不需曝露该聚合物件的内部表述方式。STL 共提供了五种 iterators 型态,以及各种衍生变化。Iterator 是 STL 中最重要最抽象的一个组件,它使容器与演算法可以各自独立发展,这是一种突破性的观念。不论就实作技术或抽象观念,iterator 都是 STL 中最关键的成份。了解了 iterators,也就进入了 STL 的大门。

 

4 function object:行为类似 function,但其实是一种 object。以实作技术而言,这是一个改写了 "call operator" 的 class。STL 提供 15 个现成的 function objects。

 

5 adaptors(调适器):用来改变(修饰)其他组件的介面。[Gamma95] 对於 adaptor 这种设计样式(design pattern)的定义是:将一个 class 的介面转换为另一个 class 的介面,使原本因介面不相容而不能合作的 classes,可以一起运作。在 STL 中,改变 function object 介面者,称为 function adaptor,改变 container 介面者,称为 container adaptor。改变 iterator 介面者,称为 iterator adaptor。例如 STL 提供的两个容器 queue 和 stack,其实都只不过是 adaptor,修饰了 deque 的介面而成就出另一种容器风貌。

 

6 allocator(记忆体配置器):容器空间配置系统。STL 提供有现成的 allocator。

 

 

 

下面这个例子,用上了 STL 的所有六种组件,目的是找出某个数列之中数值大於 40 的元素个数,答案为 4。从这个例子,你可以看到 STL 不同组件间的接合,发展到了一个怎样灵活的程度,像乐高积木一样,有无限可能。

 

#include <algorithm>

#include <functional>

#include <vector>

#include <iostream>

using namespace std;

 

int main()

{

int ia[ ] = { 27, 210, 12, 47, 109, 83, 40 };

vector<int, allocator<int> > vec( ia, ia+7 );

 

cout << count_if(vec.begin(), vec.end(),

                 not1(bind2nd(less_equal<int>(), 40)));

 

return 0;

}

// vector 是一个 STL 容器

// count_if 是一个 STL 演算法

// not1 和 bind2nd 都是 STL function adaptors

// less_equal<> 是一个 STL function object

// allocator<> 是一个 STL 记忆体配置器

// vec.begin() 和 vec.end() 分别传回两个 iterator,指向容器 vec 的头尾。

 

●软体组件分类学

 

STL 的实用价值当然很高。但是工具的使用对於技术的探究乃至学理的钻研,已属末流。要掌握 STL 的精神,乃至将来得以自行发展组件,与既有的 STL 水乳交融,就必须更深一层看看 STL 的背後学理。

 

前面说过,STL 其实是在泛型思维模式之下建立起一个系统化的、条理分明的「软体组件分类学」。这个分类学严谨定义了什麽是 concept, 什麽是 model, 什麽是 refinement, 什麽是 range,也定义了什麽是 predicate, 什麽是 iterator, 什麽是 adaptor┅。我将在此试述其中一二,让你在最短时间内对 STL 的本质有一个初步的认识。

 

⊙所谓 concept 和 model

 

所谓 concept,描述某个抽象型别的条件(或说需求,requirements)。concept 并不是一个 class,也不是一个变数或是一个 template 叁数;C++ 语言之中没有任何东西可以直接代表一个concept。然而,在每一个用到泛型程式设计方法的 C++ 程式中,concept 非常重要。由 concepts 所构成的阶层体系,正是 STL 的主体结构。

 

当某个型别满足某个 concept 的所有条件,我们便说此型别是该 conecpt 的一个model。concept 可被视为一组型别条件。如果型别 T 是 concept C 的一个 model,那麽 T 就一定满足 C 的所有条件。因此,concept 亦可被视为是一组型别。如果型别 T 是 concept C 的一个 model,我们便可说 T 隶属於「C 所表现的一组型别」。

 

举个例子,STL 规范了一些基本 concepts 如下,其中并描述了欲符合那些条件,必须以 C++ 完成哪些建设。

 

1. Assignable:型别 T 如果是 concept Assignable 的一个 model,我们便可以将 T 物件的内容拷贝并指派给型别为 T 的另一个物件,换言之型别 T 必须有一个 copy constructor。如果 x, y 都有 Assignable 性质,那麽保证以下动作中的 x, y 有着相同的值:

 

X x(y)

x = y

tmp = y, x = tmp

 

 

2. Default Constructible:如果型别 T 是 Default Constructible 的一个 model,它必须有一个 default constructor。也就是说我们可以这麽写而产生出一个 T 物件:

 

T()

 

欲产生出一个型别为 T 名称为 t 的变数,可以这样写:

 

T t;

 

C++ 的所有内建型别如 int 和 void,都隶属於 Default Constructible。

 

 

3. Equality Comparable:如果型别 T 是 Equality Comparable 的一个 model,

我们便可以这样比较两个 T 物件是否相等:

 

x==y

 

 

x!=y

 

换言之 T 必须支援 operator== 和 operator!=。

 

 

4. LessThan Comparable:如果型别 T 是 LessThan Comparable 的一个 model,

我们可以这样测试某个 T 物件是否小於另一个 T 物件:

 

x < y

 

 

x > y

 

换句话说如果某个型别能够以某种次序排列,它便隶属於 LessThan Comparable,

它必须能够以 operator< 做为比较动作,而 operator< 必须定义出某种顺序性。

 

 

 

如果某个型别同时符合 Assignable, Default Constructible, Equality Comparable 三种 concepts,我们称此型别为 regular type。大部份的 C++ 内建基本型别都是 regular types,例如 int 便是。几乎所有定义於 STL 中的型别(class templates)也都是 regular types。

 

 

现在我们看看具体的 STL 容器 vector,由哪些 concepts 衍生过来。图一是根据 STL 定义而绘制的 vector 概念衍化体系。

图一 / 根据 STL 定义而绘制的 vector 概念衍化体系

 

 

 

⊙所谓 refinements

 

如果 concept C2 供应 concept C1 的所有机能,并且(可能)加上其他机能,我们便说 C2 是 C1 的一个 refinement(强化)。

 

Modeling 和 refinement 必须满足以下三个重要特性。只要把 concepts 想像为一组型别,以下三者就很容易验证:

 

1. Reflexivity(反身性)。每一个 concept C 是其本身的一个 refinement。

 

2. Containment(涵盖性)。如果型别 X 是 concept C2 的一个 model,而 C2 是 concept C1 的一个 refinement,那麽 X 必然是 C1 的一个 model。

 

3. Transitivity(递移性)。如果 C3 是 C2 的一个 refinement,而 C2 是 C1 的一个 refinement,那麽 C3 是 C1 的一个 refinement。

 

一个型别可能是多个 concepts 的 model,而一个 concept 可能是多个 concept 的 refinement。

 

 

⊙所谓 range(范围)

 

对於 range [first, last),我们说,只有当 [first, last) 之中的所有指标都是可提领的(dereferenceable),而且我们可以从 first 到达 last(也就是说对 first 累加有限次数之後,最终会到达 last),我们才说 [first, last) 是有效的。所以,[A, A+N) 是一个有效的 range,empty range [A, A) 也是有效的。[A+N, A) 就不是一个有效的 range。

 

一般而言,ranges 满足以下性质:

 

1. 对任何指标 p 而言,[p, p) 是一个有效的 range,代表一个空范围。

 

2. 如果 [first, last) 是有效而且非空的 range,那麽 [first+1, last) 也是一个有效的 range。

 

3. 如果 [first, last) 是有效的 range,而且 mid 是一个可由 first 前进到达的指标,而且 last 又可以由 mid 前进到达,那麽 [first, mid) 和 [mid, last) 都是有效的 ranges.

 

4. 反向而言,如果 [first, mid) 和 [mid, last) 都是有效的 ranges,那麽 [first, last) 便是有效的 ranges。

 

 

有人开始吃不消了,这是数学还是编程呀?! 的确,我们面对的是一个有着数学般严谨定义的「软体组件分类学」。整个 STL 就是由这些 concepts 构成。至於真正的实作品,不过是在这样的观念基础上,以 C++ 实践出来而已。

 

●效率的疑虑

 

人们对於 STL 的最大误解是效率。事实上 STL 提供的是一个不损及效率的抽象性。泛型编程和物件导向编程不同,它并不要求你透过额外的间接层来呼叫函式;它让你撰写一般化、可复用的演算法,其效率和「针对特定资料型别而设计」的演算法旗鼓相当。每一个演算法、每一个容器的操作行为,其复杂度都有明确规范 ─ 通常是最佳效率或极佳效率。在接受规格书明定的复杂度之後,我想你不会认为自己亲手撰写的码,能够更胜通过严格检验、通行世界、无数人使用的程式库。

 

人们对 STL 效率的误解,有一大部份是把编译期效率和执行期效率混为一谈了。的确,大量而巢状地运用 templates,会导致编译器在进行 template引数推导(argument deduction)及具现化(instantiation)时耗用大量时间。但它绝不影响执行效率。至於对专案开发时程所带来的影响,我要说,和 STL 为我们节省下来的开发时间相比,微不足道。

 

STL 的严重缺点在於,它尚未支援 persistence(物件的永续性)。在良好的解决方案尚未开发出来之前,persistence 必须由使用者自行完成。

 



 

●泛型技术的三个学习阶段

 

王国维说大事业大学问者的人生有三个境界。依我看,泛型技术的学习也有三个境界:

 

第一个境界是使用 STL。对程式员而言,诸多抽象描述,不如实象的程式码直指人心。

 

第二个境界是了解泛型技术的内涵与 STL 的学理。除了前述的软体概念分类学,最好再对数个 STL 组件(不必太多,但最好涵盖各类型)做一番深刻追踪。STL 原始码都在手上(就是相应的那些表头档嘛),好好做几个个案研究,便能够对泛型技术以及 STL 的学理有深刻的掌握。

 

第三个境界是扩充 STL。当 STL 不能满足我们的需求,我们必须有能力动手写一个可融入 STL 体系中的软体组件。要到达这个境界之前,可得先彻底了解 STL,也就是先通过第二境界的痛苦折磨。

 

也许还应该加上所谓的第0境界:C++ template 机制。这是学习泛型技术及 STL 的门槛,

 

 

 

以下,我便为各位介绍六本相关书籍,涵盖不同的切入角度,也涵盖上述三个学习层次。首先的两本是着名的 C++ 百科型书籍,我只挑其中与 STL 相关的章节做介绍。为求方便,以下以学术界习惯的标示法,标示书籍代名。本文使用这些代名。凡有中文版者,我会特别加注。

 

 

[Lippman98]: C++ Primer, 3rd Editoin, by Stanley Lippman and Josee Lajoie,

Addison Wesley Longman, 1998. 1237 pages.

繁体中文版:《C++ Primer 中文版》,侯捷译, 峰 1999. 1237 页

 

[Struostrup97]: The C++ Programming Language, 3rd Editoin, by Bjarne Stroustrup, Addison Wesley Longman, 1997. 910 pages

繁体中文版:《C++ 程式语言经典本》,叶秉哲译,儒林 1999.(未录总页数)

 

[Josuttis99]: The C++ Standard Library - A Tutorial and Reference, by Nicolai M. Josuttis, Addison Wesley 1999. 799 pages

繁体中文版:侯捷计划中

 

[Austern98]: Generic Programming and the STL - Using and Extending the C++ Standard Template Library, by Matthew H. Austern, Addison Wesley 1998. 548 pages

繁体中文版:《泛型程式设计与 STL》,侯捷/黄俊尧合译, 峰 2000。548页

 

[Jjhou01]: 泛型技术 - 从具象工具到抽象思维, by 侯捷, 峰 2001. 页数未定

简体中文版:洽谈中

 

[Gamma95]: Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, Addison-Wesley, 1995. 395 pages

简体中文版:《设计模式》,李英军等译,机械工业出版社,2000. 254 页

 

●第零境界:泛型技术的基本门槛

 

在 C++ 环境中学习泛型技术,首要是把 template 相关语法与语意搞清楚。包括 class templates, function templates, member templates, specialization, partial specialization。更往基础看去,由於 STL 大量运用了 operater overloading(运算子多载化),所以这个技法也必须熟捻。

 

[Lippman98] 是一本 C++ 百科全书,向以内容广泛说明详尽着称。本书内容与 template 及 STL 直接相关的章节有:

 

chap6: Abstract Container Types

chap10: Function Templates

chap12: The Generic Algorithms

chap16: Class Templates

appendix: The Generic Algorithms Alphabetically

 

间接相关的章节有:

 

chap15: Overloaded Operators and User Defined Conversions

 

书中有大量范例,尤其附录列出所有的 STL 泛型演算法的规格、说明、实例,是极佳的学习资料。不过书上有极少数例子,因为作者的疏忽,未能完全遵循 C++ 标准,仍沿用旧式写法。更正作法可上繁体版支援网站(侯捷网站)看看。此书的学习方式以及华人读者的技术讨论也都可在繁体版支援网站上看到。

 

 

[Struostrup97] 也是一本 C++ 百科全书,向以学术权威(以及涩味极重)着称。若非具备一定程度,对於书中内容的表达方式,浅尝之下会有艰涩的口感。本书内容与 template 及 STL 直接相关的章节有:

 

chap3: A Tour of the Standard Library

chap13: Templates

chap16: Library Organization and Containers 

chap17: Standard Containers

chap18: Algorithms and Function Objects

chap19: Iterators and Allocators

 

间接相关的章节有:

 

chap11: Operator Overloading 

 

第 19 章对 Iterators Traits 技术的介绍,在 C++ 语法书中难得一见,但此为正面或负面殊难定论,因为你必须知道 Traits 技术之发展原由(问题之所在),才能够了解为什麽变成现在这般抽象模样。当然,我们不能期望一本 C++ 语言书籍对此有深刻的表现,但是这麽高阶的技术,蜻蜓点水式的说明并不会引导出阅读的兴趣,反而可能会重挫读者的信心。关於 Traits 技术,[Austern98] 表现极佳。

 

上面所说的这两本 C++ 百科全书,基本上并不是以介绍泛型技术的角度出发,而是以「C++ 涵盖了 template 和 STL,所以我介绍它」的态度出发。因此,在相关组织上,稍嫌凌乱。不过我想,没有人会因此对他们求全责备。

 

 

●第一境界:熟用 STL

 

一旦你开始学习 STL,乃至开始实际运用 STL,[Josuttis99] 绝对可以为你节省大量的翻查、叁考、错误尝试的时间。本书各章如下:

 

1. About the Book

2. Introduction to C++ and the Standard Library

3. General Concepts

4. Utilities

5. The Standard Template Library

6. STL Containers

7. STL Iterators

8. STL Function Objects

9. STL Algorithms

10 Special Containers

11 Strings

12 Numerics

13 Input/Output Using Stream Classes

14 Internationalization

15 Allocators

Internet Resources

Bibliography

Index

 

正如其副标所示「本书兼具学习用途及叁考价值」,既不夸张也当之无愧。本书涵盖的不仅是 STL,而是整个 C++ 标准程式库,详细介绍每一个组件的规格及运用方式,并佐以范例。作者的整理功夫做得非常好非常扎实。在观念解说方面,经常以图表形式让读者一目了然。

 

由於本书介绍的对象是整个 C++ 标准程式库,所以少见於其他书籍的某些组件,如 valvarry, mem_fun, ptr_fun, locales 等等,也都涵盖其中。标准程式库的几个大家伙,像是 string, iostream, locale, 也都有极具水准的介绍。本书的另一个特色是,对於与 STL 相关的各种异常讯息(exceptions),亦有介绍。这很少见。

 

[Josuttis99] 不仅介绍 STL 组件的运用,也导入 STL 的源码。这些源码都经过作者的节录整理,砍去枝节,留下主干,较易入目。这是我对此书最激赏的一部份。换句话说,阅读此书,不但进入我所说的第一学习境界,甚且进入了第二境界。这种对於白盒子(注)的阐释方式和侯捷的写作风格十分近似,我一向也喜欢繁中取简,在百万军中取敌首级。这可不是容易的动作,首先得对庞大的源码有清晰的认识,再有自己的诠释主轴,知道什麽要砍,什麽要留,什麽要注解。

 

注:附源码的工具我们称为白盒子,未附源码者我们称为黑盒子。

 

 

本书也适度介绍某些 STL 扩充技术。例如 6.8 节介绍如何以 smart pointer 使 STL 容器具有 "reference semantics" (原本 STL 容器只支援 "value semantics")。又例如 7.5.2 节介绍一个订制型 iterator,10.1.4 节介绍一个订制型 stack,10.2.4 节介绍一个订制型 queue。15.4 节介绍一个订制型 allocator。虽然都只短短一些篇幅,列出基本技法,但对於想要扩充 STL 的程式员而言,起了个头,终究是一种实质上的莫大帮助。就这点而言,本书又进入了我所说的第三学习境界。

 

 

通常,英文电脑书的字体都相当小,程式码尤其更小。本书字行之间的距离特别大些,程式码与书後索引尤然。阅读时眼睛应该轻松不少。喜欢以「页数/售价 比」来评断书籍的人,这下会高兴些了,系数高不少嘛 ─ 真像朝三暮四里头的猴子!盼望书籍轻一点薄一点便利携带的人,则可能略有愠意。我常在 BBS/News 上看到一些关於书厚薄与书价格的言论,令人发噱。有人拿侯捷的《深入浅出 MFC》比鹿挢的《未央歌》,说两本都是好书,但买一本《深入浅出 MFC》可买五本未央歌,言下之意不以为然。真是苹果比橘子,张飞战岳飞!

 

基本上谈书的价值,不应该扯上书的价格与页数。谈书的价格,则不应该扯上书的页数。我对买卖的看法是这样:电脑书不是写真集,没有用保鲜膜包起来,内容与页数都明白呈现你的眼前;你愿意买,就表示你承认它的价值匹配它的价格;否则就别买。我对书价的看法则是这样:书籍是卖知识,不是卖纸,重要的是含金量,不是页数与厚薄。如果你不愿意花比较多的钱买比较好的书,你还期望别人花比较多的精力与比较高的成本写作/出版好书吗?

 

话说远了。让我们回到主题。

 

●第二境界:了解泛型技术的内涵与 STL 的学理

 

[Austern98] 是一本艰深的书。没有三两三,别想过梁山,你必须对 C++ template 技法、STL 的运用、泛型设计的基本精神都有相当基础了,才得一窥此书堂奥。

 

本书第一篇对 STL 的设计哲学有很好的导入,第二篇是详尽的 STL concepts 完整规格,第三篇则是详尽的 STL components 完整规格,并附运用范例。

 

PartI : Introduction to Generic Programming

1. A Tour of the STL

2. Algorithms and Ranges

3. More about Iterators

4. Function Objects

5. Containers

PartII : Reference Manual: STL Concepts

6. Basic Concepts

7. Iterators

8. Function Objects

9. Containers

PartIII : Reference Manual : Algorithms and Classes

10. Basic Components

11. Nonmutating Algorithms

12. Basic Mutating Algorithms

13. Sorting and Searching

14. Iterator Classes

15. Function Object Classes

16. Container Classes

Appendix A. Portability and Standardization

Bibliography

Index

 

本书通篇强调 STL 的泛型理论基础,以及 STL 的实作规格。你会看到 conecpt, model, refinement, range, iterator 等字词的意义,也会看到诸如 Assignable, Default Constructible, Equality Comparable 等观念的严谨定义。我绝对不赞成在讨论、思考这些知识时,将这些原文术语转换为中文来使用,那会非常令自己和同伴感到困惑。所幸目前为止应该不存在这样的困扰,因为泛型的中文书籍(不论着译)非常少,不至於对那些可能不够统一、不够明确的中文术语推波助澜。

 

虽然一本既富学术性又带叁考价值的工具书,给人严肃又艰涩的表象,但本书第二章及第三章解释 iterator 和 iterator traits 时的表现,却让我不由自主地击节赞赏,大叹精彩。当我藉由这两章彻底解放了 traits 编程技术,并因而有能力观看 STL 源码(STL 源码几乎无所不在地运用 traits 技术)、撰写符合规格的自定型 STL 组件,我真不禁要激越昂扬,仰天长啸。

 

traits 是 STL 的关键编程技术。这个观念无法在这里三言两语带出,我就不多费事儿了。[Austern98] 书中那麽好的解说与导引,你自个儿去看。

 

就像其他任何 framework 一样,STL 以开放原始码的方式呈现市场。这种白盒子方式,使我们在更深入剖析其技术时(可能是为了透彻,可能是为了扩充),有一个终极依恃。因此,观看 STL 源码的能力,我认为对技术的养成与掌握,极为重要。

 

总的来说,本书在 STL 规格及 STL 学理概念的资料及说明方面,目前书市无有出其右者。我个人在元智大学开授一门「泛型程式设计」课程,[Austern98] 便是我指定给同学的高阶叁考书。本书在 (1) 泛型观念之深入浅出、(2) STL 架构组织之井然剖析、(3) STL 叁考文件之详实整理 三方面,均有卓越表现。可以这麽说,在泛型技术和 STL 的学习道路上,本书并非万能(它不适合初学者),但如果你希望彻底掌握泛型技术与 STL,没有此书万万不能。

 

 

●第三境界:扩充 STL

 

要撰写与 STL 相容 ─ 也就是可以和 STL 组件自由拼凑组装在一起 ─ 的个人自定组件,我们需要两种书籍。你已经知道,STL 的背後是一个严谨的「软体概念分类学」,为了与 STL 水乳交融,自定组件一定必须满足那严谨的架构 ─ 回头看看图一那张 vector 概念衍化图吧。因此,我们首先需要一份详尽的 STL concepts 规格书,而 [Austern98] 正是最好的(可能也是唯一的)候选人。

 

第二,万事起头难,我们可能需要足够的范例,带引我们上路。目前市面上缺乏这样的书籍。我看过 "Designing Components with the C++ STL",书名很吸引人,实则未能进入侯捷的推荐名单。幸好 [Josuttis99] 分担了部份这样的角色,聊堪倚仰。

 

 

●侯捷的理想

 

做为一个对泛型思维及 STL 产品有深刻体会的人,本身又是一名技术写作者,我不可能没有自己的论述理想。

 

由於泛型与 STL 对许多人而言,相对之下毕竟是新鲜玩意儿,语言层面的技法也还算新鲜而高阶,所以我希望为读者铺陈一条由摇篮到坟墓的路。从 C++ 相关语法、到 traits 编程技术、到 STL 的各种运用,再到 STL 的学理探究,最後示范数个扩充组件。一书而竟全功。具叁考价值的查阅型工具书我写不来(那需要完备的资料和大量的细心),观念阐述型的书籍则是我的拿手,也是我的兴趣。

 

下面便是暂名「泛型技术 - 从具象工具到抽象思维」的 [Jjhou01] 新书目录(暂定)。

 

第1章 泛型大局观(Overview of Generic Programming)

第2章 C++ 运算子多载化(Operator Overloading)基本技法

第3章 C++ 泛型基本技法 (1):Function Template

第4章 C++ 泛型基本技法 (2):Class Template

第5章 Traits 编程技法

第6章 Iterator(泛型指标)

第7章 Container(容器)

第8章 Function Object(函式物件)

第9章 Generic Algorithms(泛型演算法)

第10章 Adaptor(配适器)

第11章 Allocator(记忆体配置器)

附录 叁考书目

索引 Index

 

当你看到这篇文章的同时,我正在台湾南部的一个农村,我的老家,做这本书的最後整理。

 

 

●旁徵博引 左右逢源

 

[Gamma95] 与泛型或 STL 并没有直接关系。但是 STL 有两大类组件,却被收录於 [Gamma95] 的 23 个设计样式(design patterns)中:Iterator 和 Adaptor。其他设计样式在 STL 之中也有所发挥。两相比照,尤其是看过 STL 的源码之後,对於这些设计样式会有更深的体会,返映过来面对 STL 也会有更深一层的体会.

 

 

●一点感想

 

自从被全球软体界广泛运用以来,C++ 有了许多演化与变革。然而就像人们总是把目光放在艳丽的牡丹而忽略了花旁的绿叶,做为一个广为人知的物件导向程式语言(Object Oriented Programming Language),C++ 所支援的另一种思维 ─ 泛型编程 ─ 被严重忽略了。说什麽红花绿叶,好似主观上划分了主从,其实物件导向思维和泛型思维两者之间无所谓主从。两者相辅相成,肯定能对程式开发有更大的突破。

 

永远记住,面对新技术,程序员最大的障碍在於心中的怯弱。To be or not to be, that is the question! 不要和哈姆雷特一样犹豫不决,当你面对一项有用的技术,必须果敢。

 

--- the end

 

 

C++ / OOP大系 [转:侯捷作品]

2010年7月09日 01:30 | Comments(2) | Category:Learning Plan | Tags:

 开场白

 

《程序员》杂志邀我开一个专栏。我向来期待一本为程序员打造、以程序员为主体对象的刊物,因此这样的邀请很难推却。再加上蒋涛先生与我的私交,我於是要求自己,尽可能拨出时间来为《程序员》写稿。专栏可以开,能不能全无间断则不敢保证。

 

大陆读者对我肯定陌生,容我简介自己。我是一名资讯教育工作者,写译书籍,培训业界人员,主持网站回应读者与学员,并於大学开课。进入教育领域之前,我分别担任过台湾工业技术研究院机械所和电通所的副研究员和特约研究员,分别研发 CAD/CAM 软体和 Windows 多媒体系统。有人戏称工研院为少林寺 ─ 位在山上,男多女少,高手如云,艺成下山闯荡江湖者不计其数。

 

写这篇稿子的此刻,我投入教育领域正好十年。这是一条科技人很少想过的路子,於我也是生命中一个不经意的转弯。不过,这种迥异於软体研发也迥异於象牙塔教学的生涯,实在是多彩多姿,与读者的互动尤其曼妙无比。

 

我的钻研领域,前七年都在 Windows 编程方法和作业系统原理,近三年则放在更基础的、与平台无关的层面。

 

●书籍是永远的良师益友

 

过去十年中,有一件事最是奇特有趣:我於 1993 开始《无责任书评》专栏,介绍我所能够掌握的技术范围内的一些世界名着。这样的题材与文体,吸引了很多目光,也开创了某种先河。《无责任书评》夹杂对台湾电脑出版业的观点与评论,由於当时台湾电脑书的良窳程度极端不均(现在也是),初阶 滥而高阶贫血(现在也是),我以程序员的角度所给的评论显得尖锐不群。

 

1998 年网际网路兴盛,我把所有电脑散文都移到网路上发表,范围扩及学习方向与学习态度(但不涉及细节技术)。过去的书评文章也重新整理了起来。各位可从侯捷网站上看到所有这些文章。

 

书评之所以受人欢迎,一方面在它的知识性,一方面在它的辛辣味。通常我的原则是只评好书(该说是「荐」而不是「评」了),所以辛辣味只藏在旁徵博引的明喻暗讽之中,或偶尔忍不住的一把火。一般而言,只要有丰富的知识含量,而不是单纯地将章节照录一遍,书评专栏就够吸引人了,辛辣味只是附带红利。诸君如想尝尝真正的川辣子,看看国外期刊的书评,肯定叫温良恭俭让的中国人频频抚胸,大惊失色。

 

好书之於学习(尤其是自修),重要性自不待言,所以书评永远受欢迎。好书是一支钓杆,好书评则让你认识这支钓杆并告诉你到哪儿去买。单一书评固然好,如果能系列化、系统化、根据技术的演进与层次,铺陈一条学习的红地毯,就更有价值。过去我曾经分篇为台湾读者介绍过 C++/OOP 方面的许多好书。做为本专栏的第一篇,我决定将它们汇总结集,让你一次看饱买足。

 

 

●阅读之前

 

往下阅读之前,我想先谈一些打底的话。

 

第一,以下介绍的全都是外来书。各位购买这些书籍或许有经济压力,但毕竟它们都是成名已久的世界名着,我想,为读者开这扇窗绝对是很重要的。

 

购买这些书籍其实很方便,只要你有信用卡,连上亚马逊网路书店(www.amazon.com)爱怎麽买就怎麽买。我们的困难可能在於信用卡和书价。唔,加上运费真的很贵。

 

第二,在我少不更事的时候,读了一本好书并不会回头特别记下作者姓名。这是个绝对错误的态度。茫茫书海中该如何选书?第一次当然是到书店去乱枪打鸟,浪费一点子弹。但是你不能老停留在少不更事的阶段,你的子弹还有你的书架空间都很宝贵,你的时间更宝贵。牢记优秀作家的名字,是找好书的捷径。这其实也是写阅环境的一个进步表徵:让好作家有自己的品牌。

 

第三,OO(Object-Oriented,物件导向)领域,从编程到设计,可概分为 OOP(Progrmming)、OOA(Analysis)、OOD(Design)。目前国外十分成熟的 UML(Unified Modeling Language)属於OO 领域里头用来将设计概念表现出来的一种 notation(符号表现法)。本篇文章只介绍到 OOP 这个层次(唯 [Gamma95]稍属例外),这比较具体,也比较贴近大部份程序员。愈往上去愈抽象,愈接近软体工程或方法论。

 

第四,以下介绍的这些 C++/OOP 书籍,几乎成为我初步判断一个人在这方面水平的基准。一个具备数年经验的 C++ 程序员,或许自己能够摸索出「总是让 base class 拥有 virtual destructor」这样的准则,但初出茅庐的程序员,恐怕连 virtual destructor 是什麽都不甚有概念,更别说该如何正确运用它。如果他说他看过 [Meyers98],我会比较放心他的水平。

 

有趣的是,我曾经在自己班上(学生从大二到研究生都有)做了一个调查。拥有这些书籍的学生人数并不多,而且老是同样几位。这让我感觉,强者恒强弱者恒弱,悲夫。就我和业界的广泛接触经验,我也发现,许多程序员离开学校後就不太看书了,或者因为忙碌,或者因为安於现况。专案做了不少,技术却没有精进太多。三两下招数一再用老,人特别容易空乏。请你打开这扇窗,你会发现巨着之所以为巨着,专家经验之所以为专家经验,是有道理的。愈是看了这些书,你愈会发现这些书的价值,并觉醒过去的一些愚蠢行为。

 

第五,下面开出来的书单都是我熟读过的,其中甚且不少繁体中文版是我翻译的,所以我放心推荐并接受质询。然而书海浩瀚,遗珠难免。

 

第六,为求方便,以下以学术界习惯的标示法,标示书籍代名。文中即使用这些代名。凡有中文版者,我会特别加注。

 

[Ellis90]: The Annotated C++ Reference Manual, by Margaret A. Ellis and Bjarne Stroustrup, Addison-Wesley, 1990. 447 pages.

 

[Gamma95]: Design Patterns: Elements of Reusable Object-Oriented Software,

by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, Addison-Wesley, 1995. 395 pages

简体中文版:《设计模式》,李英军等译,机械工业出版社,2000. 254 页

 

[Lippman98]: C++ Primer, 3rd Editoin, by Stanley Lippman and Josee Lajoie,

Addison Wesley Longman, 1998. 1237 pages.

繁体中文版:《C++ Primer 中文版》,侯捷译, 峰 1999. 1237 页

 

[Lippman96]: Inside the C++ Object Model, by Stanley Lippman, Addison Wesley Longman, 1996. 280 pages

繁体中文版:《深度探索 C++ 物件模型》,侯捷译, 峰 1998. 320 页

 

[Meyers96]: More Effective C++, by Scott Meyers, Addison-Wesley, 1996. 318 pages

繁体中文版:《More Effective C++ 中文版》,侯捷译,培生 2000. 318 页

 

[Meyers98]: Effective C++, Second Edition, by Scott Meyers. Addison Wesley Longman, 1998. 256 pages

繁体中文版:《Effective C++ 2/e 中文版》侯捷译,培生 2000. 256 页

 

[Struostrup97]: The C++ Programming Language, 3rd Editoin, by Bjarne Stroustrup, Addison Wesley Longman, 1997. 910 pages

繁体中文版:《C++ 程式语言经典本》,叶秉哲译,儒林 1999.(未录总页数)

 

[Sutter99]: Exceptional C++, by Herb Sutter, Addison Wesley Longman, 2000. 208 pages

繁体中文版:《Exceptional C++ 中文版》侯捷译,培生 2000. 248 页

 

●层级一:语法/语意(C++)

 

学习语言,当然首先从语法开始。初学者究竟要从轻松小品出发,或一开始就接触巨着,殊无定论,因为初学者有很多种,「初学者」一词却无法反映他们的真实状态。我的学生群中有 13 岁的,也有 31 岁的(年纪更大的当然也有),有人连电脑基本概念都尚未建立,有人已是经验丰富的软体工程师。不同背景、不同年龄、不同领悟力、不同学习速度的人,需要不同层级的教材来满足他们。同一个人在不同阶段,也需要不同层面的教材来提升其功力与视野。

 

但,不论新生或老手(新生有一天会变成老手),任何一位C++ 程序员,我都强烈建议你的书架上要有 [Lippman98] 和 [Struostrup97] 这两本书。它们是 C++ 语法/语意层面的百科全书;所有相关问题,这两本书都是最後仲裁,说了算!它们不适合连电脑基本概念都缺乏的人,但颇为适合已有编程经验的人。

 

这两本书都已经在 C++ 领域驰逞十年。最新版本都是第三版,印映 1998 定案的 C++ 标准规格。由於 C++ 标准规格带入一个十分庞大的标准程式库,所以这两本书也都比其前一版有巨幅的改变。如果要拿这两本书做特性比较,我的个人观感是,[Lippman98] 适合做为教本,教学自修叁考皆宜,[Struostrup97]比较生涩难读,学术味重,叁考性浓厚,权威性最高(毕竟 Struostrup 创造了 C++)。

 

这两本书的每一版寿命大约是五年。C++ 标准规格定案後,国际标准组织(ISO)每五年开会覆审一次,所以第三版至少也是五年寿命。也许有人以为,做为疑问辩论的最终裁判,还是以 C++ 规格书为准(1998/09/01 出版,编号 ISO/IEC 14882),而且网路下载只需 18 美元,PDF 格式,索引极为方便。这当然是很好的一份工具,但是这份文件绝不适合做为学习材料,太硬了。

 

目前全世界还没有任何一个 C++ 编译器支援完整的 C++ 标准规格,互有长短,所以晚近新增的语言特性,不见得能够在你手上的编译器演练。关於 Visual C++, Borland C++, CYGNUS C++ 三套工具在C++ 标准规格上的表现,我个人有一些经验,整理於http://www.jjhou.com/qa-cpp-primer-27.txt。为什麽迟迟未有完全支援标准规格的 C++ 编译器问世呢?因为这已经不成为市场竞争重点;C++ 开发工具市场已经转到对视窗介面的支援以及对企业的完整解决方案。

 

这两本书深具工具叁考价值,因此索引格外重要。两本繁体版译本皆用心地制作了索引(仍以英文术语来排列),此可为大陆借镜。中文电脑书带有索引,在台湾亦不常见,两本译本皆因译者的特别用心才得如此。索引采用英文术语,导出一个问题:如果书内文本没有保留英文术语,怎麽办?索引的制作与配套办法,是科技翻译亟需深思的一个问题。我的作法是,把诸多科技术语保留原文不译,并努力维持中英页页对照,这麽一来原书索引就可以完整而轻松地保留下来。保留原文术语不译,不完全是为了索引的制作,而是因为某些字眼强译为中文,不但与业界习惯脱节,也与世界脱节。我所采行的这种作法受到很多读者的喜爱,但是哪些原文术语要保留,哪些要中译,又是见仁见智。大凡如果译者真正是业内人士,他的选择不会脱离业界习惯太远。

 

●层级二:专家经验(C++/OOP)

 

能够在学习语法并开始练习编程的同时,就接触专家的经验,最是理想,但实际上很难如此。一方面,每一条经过淬炼的编程规则,其来龙去脉可能牵涉到多方面的知识,甚至可能涉及底层技术,这对新手的负担过重。另一方面,初学者往往只顾眼前半亩田,眼光没太高远。不过,如果有良师带引,依样画葫芦不失为一种初期的权宜学习方式。

 

无论如何,为了提升自己的 OOP 功力,专家经验是一条终南捷径,让你一次吸取高手十数年功力。[Meyers96] 和 [Meyers98] 是我极为推荐的两本专家经验书。以下试摘书中条款数例,诸君可掂掂自己的斤两,看看自己平时实践了多少,从各条款中又联想了多少。

 

⊙以下摘自 [Meyers98]:

 

条款1:尽量以 const 和 inline 取代 #define

条款2:尽量以 <iostream> 取代 <stdio.h>

条款3:尽量以 new 和 delete 取代 malloc() 和 free()

条款5:使用相同型式的 new 和 delete

条款6:记得在 destructors 中以 delete 对付 pointer member

条款7:为记忆体不足的状况预做准备

条款8:撰写 operator new 和 operator delete 时,应奉行惯常行为

条款9:避免遮掩了 new 的正规型式

条款10:如果你写了 operator new,请对应写一个 operator delete

条款11:classes 内如果动态配置记忆体,请为它宣告一个 copy constructor 和一个 assignment 运算子

条款12:在 constructor 中尽量以 initialization 动作取代 assignment 动作

条款13:initialization list 中的 members 初始化排列次序应该和其在 class 内的宣告次序相同

条款14:总是让 base class 拥有 virtual destructor

条款15:令 operator= 传回 *this 的 reference

条款16:在 operator= 中为所有的 data members 赋值。

条款17:在 operator= 中检查是否「自己赋值给自己」

条款19:区分 member functions, non-member functions 和 friend functions 三者

条款20:避免将 data members 放在公开介面中

条款21:尽可能使用 const

条款22:尽量使用 pass-by-reference(传址),少用 pass-by-value(传值)

条款23:当你必须传回一个 object 时,不要尝试传回一个 reference

条款29:避免传回内部资料的 handles

条款30:避免写出「传回 non-const pointers 或 references 并以之指向较低存取层级之 members」的 member functions

条款31:千万不要传回「函式内的 local 物件的 reference」,或是「函式中以 new 获得的指标的所指物件」。

条款32:尽可能延缓变数定义式的出现

条款33:明智地运用 inlining

条款34:将档案之间的编译相依关系(compilation dependencies)降至最低

条款35:确定你的 public inheritance 模塑出 "isa" 的关系

条款36:区分「介面继承(interface inheritance)」和「实作继承(implementation inheritance)」

条款37:绝对不要重新定义一个继承而来的非虚拟函式

条款38:绝对不要重新定义一个继承而来的预设叁数值

条款39:避免在继承体系中做 cast down(向下转型)动作

条款40:透过 layering(分层技术)来模塑 has-a 或 is-implemented-in-terms-of 的关系

条款41:区分 inheritance 和 templates

条款42:明智地运用 private inheritance(私有继承)

条款43:明智地运用多重继承(multiple inheritance,MI)

条款45:知道 C++(编译器)默默为我们完成和呼叫哪些函式

条款47:使用 non-local static objects 之前,确定它已有初值

条款49:尽量让自己熟悉 C++ 标准程式库

 

 

⊙以下摘自 [Meyers96]:

 

条款1:仔细区别 pointers 和 references 

条款2:最好使用 C++ 转型运算子 

条款3:绝对不要以 polymorphically(多型)方式来处理阵列

条款4:非必要不使用 default constructor

条款5:对自定的型别转换函式保持警觉

条款6:区别 increment/decrement 运算子的前序(prefix)和後序(postfix)型式

条款7:千万不要多载化 &&, ||, 和 , 运算子

条款8:了解各种不同意义的 new 和 delete

条款9:利用 destructors 避免遗失资源

条款10:在 constructors 内阻止资源遗失(resource leaks)

条款11:禁止异常讯息(exceptions)流出 destructors 之外

条款12:了解「丢出一个 exception」与「传递一个叁数」或「呼叫一个虚拟函式」之间的差异

条款13:以 by reference 方式捕捉 exceptions

条款15:了解异常处理(exception handling)的成本

条款17:考虑使用 lazy evaluation

条款18:分期摊还预期的计算成本

条款19:了解暂时物件的来源

条款20:协助完成「传回值最佳化(RVO)」

条款21:利用多载化技术(overload)避免隐式型别转换

条款22:考虑以运算子的复合型式(op=)取代其独身型式(op)

条款24:了解 virtual functions、multiple inheritance、virtual base classes、

runtime type identification 所需的成本

条款25:将 constructor 和 non-member functions 虚拟化

条款26:限制某个 class 所能产生的物件数量

条款27:要求(或禁止)物件产生於 heap 之中

条款28:Smart Pointers(精灵指标)

条款29:Reference counting(叁用计数)

条款30:Proxy classes(替身类别、代理人类别)

条款31:让函式根据一个以上的物件型别来决定如何虚拟化

条款33:将非尾端类别(non-leaf classes)设计为抽象类别(abstract classes)

 

其中条款

 

[Meyers96] 和 [Meyers98] 这两本书成名已久,获得极大的声誉。以下的赞美可以使你更了解这两本书的性质和价值:

 

◆在你开始着手第一个真正的 C++ 专案之前,你应该阅读本书;在你获得一些实务经验之後,你应该再读一遍。-- comp.lang.c++

 

◆作者不只提供你撰写 C++ 码时应该遵循的明白规则,也提供了深入的解释与范例。-- Sun Expert

 

◆每一位 C++ 程式员不只应该拥有这本书,而且应该确实运用这本书。书中文字极易拿来实际运用,交叉叁考与索引的功夫做得很好。-- Computer Language

 

◆这本绝妙好书提供的招数,帮助我们把 C++ 运用得更好。每一位 C++ 程式员桌上都应该有这本书。在提升 C++ 程式设计的整体品质上, Scott Meyers 这份珍贵的礼物或许比业内任何人士的贡献都大。-- Jesse Liberty, C++ Report

 

 

[Sutter99] 是另一本专家经验谈。作者是 C++ Report 期刊主编,并主持网路上一个名为每周之星(a Guru of the Week,GotW)的 C++ 特别节目。以他的背景和经历,接触的疑难杂症自然是又多又猛。这本书整理了 47 个条款,由於条款名称无法表现某种具体准则,所以我不条列於此。本书主要分为八大项:

 

1. 泛型程式设计与 C++ 标准程式库

2. Exception-Safety(异常发生时仍安全)的主题与相关技术

3. Class 的设计与继承

4. 编译器防火墙(Firewalls)及 Pimpl 惯用手法

5. 名称查询、命名空间、介面原则

6. 记忆体管理

7. 陷阱、易犯错误与有害作法

8. 杂项主题

 

Scott Meyers 为此书所写的序,点出了这本书的特质:

 

『从语言的特性到标准程式库内的组件,再到程式编写技术,本书在不同的主题之间跳跃,总是使你稍稍失去平衡,总是使你必须付出全然的注意力。...我把 GotW 发音为 "Gotcha"(意思是「这下可逮到你了」),或许很适当。当我把书中测验的(我的)答案拿来和 Sutter 的答案比较,我掉进他(和 C++)铺设的陷阱中 ─ 虽然我实在不想承认这点。我几乎可以看见 Herb 微笑并温柔地对我所犯的每一个错误说 "Gotcha!"。...当你选择 C++ 做为工具,你必须小心地思考你正在做些什麽。C++ 是一个威力强大的语言,用来协助解决吃力的问题,其重要性使你必须尽可能面对语言本身、程式库、程式惯用手法来磨炼你的知识。』

 

就我的英文程度而言,[Sutter99] 读起来不若 [Meyers96] 和 [Meyers98] 那般平顺,原因是其中用了很多厘语、口语、典故。举个例子,Morphy law 是什麽,大家知道吗?(莫菲定律说:会出错的,一定会出错。)Machiavelli 又代表了什麽意思?(意大利政治家,以诈术闻名。)

 

这类专家经验谈,多半薄而贵,但贵得有价值。好消息是,[Meyers96] 和 [Meyers98] 已经集结为电子书,以光碟呈现,采用 HTML 格式,可使用任何支援 Java(以便进行全文检索)的浏览器阅读。多少钱一片?请上亚马逊瞧瞧。

 

 

●层级三:底层机制(C++ Object Model)

 

如果对於迥异传统编程方式的 C++ 特性,诸如 virtual functions、constructors、destructors┅等特异功能一直无法心领神会,可能有必要到内部机制去深度游历一番。不要以为钻到这麽深层的技术,会愈搞愈糊涂,愈搞愈恍忽。很多人,包括我自己,是在游历过底层机制一遍之後,才彻底觉悟并接受了 C++。

 

所谓底层机制主要是指 (1) object 的记忆体布局:data members 分布在哪里?加了 static 又如何?member functions 分布在哪里?加了 virtual 又如何?有了继承又如何?(2) constructors 和 destructors为什麽会自动被唤起?(3) template 模板机制是怎麽回事?(4) this 指标是怎麽回事?(5) runtime type identification(RTTI)是怎麽实作出来的?

 

知道了这些底层机制,你便能够对自己在 C++ 程式中的每一个动作所引发的影响,了如指掌。学习这些底层技术,不是为了自行开发一套编译器,而是为了彻底掌握 C++ 语言;底层技术的学习,只是过程,不是目标。这种情况和《深入浅出 MFC》(侯捷着,松岗 1997)的情况很像,数万名读者不是为了自行开发 framework 而欢喜阅读该书对 MFC 的剖析,是为了彻底掌握自己在撰写 MFC 应用程式时的一言一行。

 

底层机制方面的专论书籍非常稀少。我所仅见的两本,一是 [Ellis90],一是 [Lippman96]。前者被昵称为 ARM(带注解的叁考手册),是早期 C++ 编译器的实作依循准则,但因年代过远,我宁愿更推荐後者。[Lippman96] 笔误非常多,我翻译此书的过程中至少修正了100 个以上的笔误。

 

了解事务的本质,到底有没有必要?这个问题太简单了:如果你必须走那麽一遭,才能接受事务的表徵,那麽於你就有必要。如果你天生是个 OO 奇才,或你一开始接触的第一个语言就是OO 语言,以至於有可能认为其中的一切都是理所当然,可以完全领受各种特性的运用,那麽底层机制於你就不需要。

 

我个人是如此地真正第一线面对大量的学习者,就我的教学经验(乃至於我个人的学习经验),我要说,了解事务的本质,对绝大多数人都有极正面的帮助。关於这一点,我最喜欢引用林语堂先生在《朱门》一书里头的一句话:『只用一样东西,不明白它的道理,实在不高明。』

 

●层级四:设计观念的复用(C++/Patterns)

 

软体工程的所有努力,无非是为了美好的复用性(reusibility)。从早期的subroutines, procedures, functions, 到後来的 classes, templates。在在为了相同的目标。如今我们已经能够将「资料,以及处理资料的动作」封装得很好,甚至能够把资料型别都抽取出来成为叁数,甚至更进一步将资料本体和处理资料的各种演算法独立开来,各自发展而又能够藉着某种「黏胶」彼此作用(注)。

 

注:这便是所谓泛型编程(generic programming)的精神。下个月我为大家介绍这个主题。

 

很好,很好。但是长久以来我们却无法将设计概念以规格化的方式传承下去。面对资料结构(data structures),我们只要说 stack, queue, list, 不必多言,闻者马上就知道stack 是先进後出,queue 是先进先出,list 是单向或双向串链。面对演算法(algorithms),我们只要说 quick-sort 和 binary-search,不必多言,闻者马上就知道其复杂度分别是 O(N log N) 和 O(log N),其行为模式如何如何。但是当我们希望保证某个 class 在整个系统中只有一份 object 时,该如何设计?当我们希望对某个 object 架构出一个替身(或说代理人)以控制对本尊的存取(进而达成缓式评估lazy-evaluation)时,该如何设计?当我们希望以某种方法走过某个聚合物件内某一范围的所有元素,而不需曝露该物件的底层结构时,该如何设计?当我们希望以共享方式来处理系统中的基本元素(例如庞大文档内数量相对极少的基本字符)时,该如何设计?

 

如果这些一再被反覆大量运用、并且早经众人淬炼出极佳作法的设计(一整组解决方案),能够系统化地分类整理,给定标准名称、定义、效果、实作法、甚至示例代码,我们就不必每次都从轮子造起(还造得不比专家圆呢)。如果程序员之间只要说Singleton, Proxy, Iterator, Flyweight,闻者马上知道其背後代表的是某种特定设计,有着特定的逻辑,用以解决某种特定问题,可多好。这正是将设计观念及其实作逻辑的宝贵经验,以简洁而可复用的形式表达出来。

 

[Gamma95] 一书内含精心整理的 23 个 design patterns。四位作者的主要贡献不在於 patterns 的创建,而在於 patterns 的整理形式与发扬光大。书中所提的 patterns 名称,几乎已经成为 OO 设计领域里头的标准辞汇。《程序员》去年 11 月份有一份蒋涛先生针对 [Gamma95] 的评论,其中对於 patterns 的比喻,令人激赏:『patterns 需要反复练习体会,才能应用自如。这有点像围棋中的定式,围棋定式是百年来高手下法的总结,但不能简单地应用,要看场合选择合适的定式,还要按棋理变通下法。』

 

幸运,真幸运,[Gamma95] 也有电子书出版,以光碟呈现,采用 HTML 格式,可使用任何支援 Java(以便进行全文检索)的浏览器阅读。

程式设计究竟是一门工匠技艺还是一门艺术?都可以是!看你从哪个角度出发。有人说连设计概念都可以以 patterns 规格化地传承,还谈什麽艺术?如果你是这样想,我说三件事给你听。建筑是一门技术还是艺术?很多人都认为建筑是一门艺术。然而 patterns 的概念正是滥觞於建筑设计领域。目前软体界所使用的 pattern 一词源自建筑理论大师 Christopher Alexander 的着作,他的书探讨的虽然都是建筑设计与都市规划的课题,但其精神与本质却适用其它领域,包括软体开发。另一件事是,软体界开始流行 framework 工具时,也有人认为程式主框架都被限死了,谈什麽设计与弹性?我说:只要馒头好吃,我从不在乎是机器馒头还是纯手工精制。你的设计精力应该放在专业领域如绘图、影像处理、统计、数学分析┅,而不是放在共通的基础框架上。如果你真的对基础共通的事务感兴趣,你不应走应用软体开发之路,应该将设计天份用来研究更新更好的资料结构,更新更好的演算法,更新更好的框架。

 

 

●线性学习?没的事!

 

虽然我把 C++/OOP 的学习阶段分为四层,但除了第四层得万事俱备才能水到渠成,其他三层的学习并不是那麽泾渭分明。通常你要你的C++ 坚轫锋利,你得让它历经多次回火,在高热和骤冷之间 炼,在学术与实用之间震荡。我无法为你画出一条单行道,你势必得走些回头路,时而品味一下曾经忽略的小花,时而啜饮一口被你遗忘的甘泉,填实了某种缝隙之後,才能神清气爽充实盈满地再出发。

 

-- the end25~31层次甚高,用来解决C++ 软体开发过程中一再出现的问题,作者把这类问题及其解法称为 idioms(惯用法)或 patterns(样式),与着名的23个精典 patterns(见 [Gamma95])相呼应。虽然这里所谈的规模格局部都比较小,但正因为如此,作者得以完成比较具体的实现,反而比 [Gamma95] 容易阅读。

 

《Exceptional C++》------ ( 2 )

2010年7月09日 01:08 | Comments(1) | Category:Reading Notes | Tags:

条款24:使用/滥用继承

(1):non-public inheritance表示根据某物实作出, Containment标识has-a关系,再隐含表达了根据某物具现出的意思。

    inheritance是single containment的超集,无法表达出 N containment的意义。

    提倡在能使用aggregation( 聚合 )时,不要使用继承的方法.

(2):什么情况下会使用non-public inheritance:

    a),需要改写虚函数,和利用基类的protected成员.

    b),有生命期的问题,符合基类和子类的构造和析构的生命期特征.

    c),有虚继承关系时,需要使用不同的虚基类初始方法.

    d),基类具有 EBCO 性质时.  Empty Base Class Optimazation:当空类作为基类时,只要不会与同一类型的另一个对象或子对象分配在同一地址,就不需为其分配任何空间.

    e),在子类的member function和 friend中, 可以使用多态.

(3):public inheritance表示Is-a的关系和Work-Like-a的关系,符合LSP法则.所有改写的Member function,都必须不要求更多,也不要求更少(语义,功能代码的一致).

    public inheritance并不是为了代码重用这一个简单的目的,它的主要目的为了被既有码以多态形式重复使用base object.

 

条款25:面向对象程序设计

(1):一些关于C++语言的特色的讨论.

 

条款26~28:编译期的依赖性

(1):前置声明的使用

(2):采用struct XImpl的方法,来封装类的私有成员.可避免编译依赖性.

 

条款29:编译级防火墙

Impl方法:把类的私有数据封装成另外一个结构体,而在类中,以一个结构体的指针指向.(隐藏实现细节,提高封装性)

(1):Impl方法的代价: a),给指针的分配内存操作; b),每一个隐藏成员需要一个间接层才可以访问到.

(2):比较好的做法,把所有的私有成员数据和函数都放入Impl中, 并在Impl中存储一个反向找到类的指针.

 

条款30:"Fast Pimpl"技术

(1)一些关于如何隐藏实现细节类,而使用operator new类似方法的东东.

 

条款31:名称搜索

(1):Koenig Lookup:如果给函数提供了一个class类型的实参,那么在名称搜索时,编译器会认为包含那个class的命名空间内地同名函数也是候选函数.

 

条款32~34:接口原则

(1):接口原则: a),对于classX,所有的函数,包括非成员函数,只要满足提到X并且与X同期提供,就是X的接口,是X的逻辑组成部分.

     b),成员和非成员函数都是X的接口, 只不过成员具有更强的关联关系而已.

              c),同期出现,可解释为出现在同一个头文件或者同一个命名空间中.

 

条款35~36:内存管理

(1): 常量数据区:存储字符串等在编译期可以确定的值.  在整个程序生命期,区域中的数据都是可见的,并且只是只读的.

(2): 堆,栈,全局/静态区,自由区,略...

(3): 注意new和delete, new[]和delete[], operator new和operator delete的配对使用.

(4): 对于数组,不要使用多态; 对于继承关系, 析构函数要为虚函数.

 

条款37:auto_ptr

(1): auto_ptr拷贝时,将移交所有权.

(2): 可以声明const auto_ptr<CA> ptr( new CA ), 这样的const auto_ptr在拷贝时会提示错误.

 

条款38:对象等同问题

(1): 对象指针地址的比较,并不是任何时候都可信度.

 

条款39:避免自动转换

(1): 隐式转换一般不安全, a) 它会影响重载解析  b) 它会让错误的代码在转换后安静的通过编译.

(2): 应该避免书写转换运算符和单一参数的构造函数.

 

条款40~41:对象的生命周期

(1): 可以编写一个私有函数来共享拷贝构造和拷贝赋值代码, 不要利用使用显式析构,再placement new的方法来达到目的.

(2): 将赋值函数声明为 T& T::operator=( const T& t ), 返回T& 而不是 const T& 会衍生 ( a = b ) = c 这样的用法问题, 但是带来的好处是

     T可以应用到STL的容器中, 因为STL的容器要求 T 的赋值必须返回T&.

 

 

条款42:变量的初始化

(1): SomeType t........调用默认构造函数SomeType::SomeType()

     SomeType t()......其实是声明了一个函数

     SomeType t( u )...拷贝构造

     SomeType t = u....也是一个拷贝构造, = 是假象, t在这里不是被赋值, 而是被构造

 

条款43:正确使用const

(1): 函数返回一个对象时, 尽量使用 const 修饰.

(2): 对于一些改变了内部私有状态的成员函数, 如果对外的逻辑是const的, 依然要声明为const.( 它修改的东西用mutable修饰就可以了 )

 

条款44:正确使用转型

(1): 所有指针都可隐式转换为 void*。

(2): 向下转型使用dynamic_cast,可以汇报错误( 可以在确定的情况下, 使用static_cast提高效率, 但是如果错误就没办法知道), 向上转型是一种默认的隐式转换.

(3): 对于互不相干的指针转化, 多使用 reinterpret_cast.

(4): dynamic_cast只对public inheritance有效, 可以穿越继承层次交叉转型( 多继承 ).

 

条款45:bool类型

 

条款46:转呼叫函数

 

条款47:控制流

即代码的执行顺序

(1)以下要素都可以影响代码的执行顺序: a),全局变量的初始化顺序 b),函数的参数传参顺序, 执行顺序 c),代码的异常处理

 

 

《Exceptional C++》------ ( 1 )

2010年7月08日 01:45 | Comments(3) | Category:Reading Notes | Tags:

 条款1:迭代器

(1):无效的迭代器不可提取值, 例如 *end();

(2):对于成对待迭代器注意安全性,是否属于同一容器和是否先后顺序正确( STL的算法不检查这些安全 );

(3):注意迭代器失效的问题;

 

条款2~3:如何设计不区分大小写的string类

(1):注意string的定义, typedef basic_string<char, char_trait<char>, allocator<char> > string, 只需要继承一个 class my_char_trait : public char_trait<char>

    (字面翻译: 改写 字符特性( char trait ) )

 

条款4~5:具有最大通用性的复用容器

(1):模板构造函数和赋值函数,不会掩盖默认的隐藏的构造函数和赋值函数.

(2):要实现模板类的复制构造和赋值,需要重新实现特殊的成员模板函数,然后调用它( 与默认的形式是不一样的 ).

 

条款6:临时对象

(1):函数const reference传值,  函数单一入口单一出口,出口处采用显式构造函数返回对象, 使用++i而不是i++.

 

条款7:尽可能使用STL中的算法.

 

条款8~17:异常处理的安全性( difficult and fussy ).

 

条款18~19:代码的复杂性

(1):异常处理,会极大的增加代码的调用路径和复杂性。

 

 

 

条款20: class 设计技术

(1):避免单一参数的构造函数( 包括提供了N-1个默认参数的N参数构造 ),这样会造成隐式转换。

(2):a),函数参数传递采用by const reference not by object; b) 对于a = a + b, 如果支持应写成 a += b, 对于 a,b是非内置类型时,效率提升。

(3):对于运算符重载, 如果编写了 +, 应该同样编写 +=, 并且 + 通过 += 实现, 功能完整,且易于维护.

(4):操作符重载: 一元操作符应该是member function;

形如一元的, =, (), [], -> 应该是member, assignment版操作符( +=, -=, ... )应该是Member function

                其他的二元操作符应该是nonmember的.

(5):对于操作符重载,要注意函数的返回类型. 例如 +, 应该返回 const object, 避免 a + b = c出现, 例如 operator <<, 应该返回 stream&, 以便链式等式。

(6):对于类成员名称, 不要使用下划线开头, 例如 _real, 编译器的很多保留字采用这样的命名。

 

示例类:

 

//复数类

class CComplex 

{

public:

	explicit CComplex( double real, double imaginary =  0 )

		: m_dbReal( real ), m_dbImaginary( imaginary )

	{



	}



	CComplex& operator+= ( const CComplex& cref )

	{

		m_dbReal += cref.m_dbReal;

		m_dbImaginary += cref.m_dbImaginary;

		return *this;

	}



	CComplex& operator++()

	{

		++m_dbReal;

		return *this;

	}



	const CComplex operator++( int )

	{

		CComplex tmp( *this );

		++( *this );

		return tmp;

	}



	ostream& PrintSelf( ostream& os ) const

	{

		return os<<"("<<m_dbReal<<","<<m_dbImaginary<<")";

	}



private:

	double m_dbReal;

	double m_dbImaginary;

};



const CComplex operator +( const CComplex& lhs, const CComplex& rhs )

{

	CComplex tmp( lhs );

	return ( tmp += rhs );

}



ostream& operator<<( ostream& os, const CComplex& cref )

{

	return cref.PrintSelf( os );

}

 

条款21:改写虚函数

(1):在发生了隐藏时, 可以采用 using CBase::Func, 显式的声明父类函数。

(2):虚函数的调用时动态绑定,但是函数的默认参数值,是静态调用, 所以改写虚函数时,不要修改父类的默认参数值.

 

条款22:类之间的关系-1

(1):public inheritance 代表的是 Is-a 关系, 遵循Liskov替换法则。

LSP--Liskov Substitution Principle

定义:如果对于类型S的每一个对象o1,都有一个类型T的对象o2,使对于任意用类型T定义的程序P,将o2替换为o1,P的行为保持不变,则称S为T的一个子类型。

子类型必须能够替换它的基类型。LSP又称里氏替换原则。

(2):对于要塑造一个 A 是由 B 的实现而产生作用, 应该使用 成员/聚合, 或者私有继承实现。

    私有继承出现的场合:是需要Protected成员或者需要修改虚函数的实现。

条款23:类之间的关系-2

(1):关于一些设计模式思想的。

 

Current Plan

2010年6月18日 01:47 | Comments(0) | Category:Learning Plan | Tags:

 Code:

 3, C::B && wxWidgets, Calculator

 4,Hook, PS & CAD的接口

5,QT

 

 Book:

 1 More Exception C++

 2,Design Pattern...

 3,Direct Show方面的

 

 

《More Effective C++》------ ( 4 )

2010年6月10日 20:27 | Comments(2) | Category:Reading Notes | Tags:

条款21:通过重载避免隐式转换

(1):C++规定,重载operator时,必须有一个用户自定义类型,即内建类型不允许修改默认的操作符意义。

(2):通过多种形似的操作符重载函数,明确声明需要的类型,可以避免隐式类型转换。

 

条款22:考虑运算符的赋值形式( op= ),代替它的单独形式( op )

(1):把 +, - 通过+=, -=来实现, a), 易于维护,只需要维护+= 和 -= 代码 b) +=, -=是效率更高的类型

 

条款23:考虑变更程序库

 

条款24:理解virtual function, multi derived, virtual base 和 RTTI所需要的代价

(1):虚函数的代价: a) 类多出来一个 vurtual table空间消耗,  实现时, vtbl是否在编译的obj中具现,一是在使用类的所有obj中具现 二是在包含该类的第一个非内联非纯虚 虚函数的实现体。   b) 类对象必须携带以个 vurtual table pointer。  c) 虚函数放弃了 inline 功能。 (动态函数和静态替换时冲突的)

(2):多继承和虚基类的代价: 除了携带自己的vtbl外,还得为基类携带一些特殊的vtbl。

(3):RTTI的代价: 在vtbl的 0 位置,存储了类的 type_info的地址。( 一般化实现方法)

 

----------------------------------------------------------------------------------------------------------------------------------------------------------------

----------------------------------------------------

技巧( Techniques, 又称Idioms or Pattern )

 

 

条款25:将构造函数和非成员函数虚拟化

(1):一种虚拟技巧, 用以个非虚拟函数,去调用一个private或者protected的虚拟函数。

 

条款26:限制类产生的对象的个数

(1):对象0个或者1个.....Singleten模式。  隐藏构造函数,采用局部静态变量出现

(2):带有局部静态变量的函数,不要使用Inline,   内部链接的函数,可能引起引起复制(包括了局部静态变量)。

(3):对象所处的三个环境: 普通类对象,作为其他类的基类,被其他类包容。

(4):引用计数思想

 

条款27:要求或者禁止在堆中产生对象

(1):禁止在栈中创建对象: 把析构函数设为 private。但是必须提供成员函数delete。

   class COnlyHeap

{
public:
    COnlyHeap() {}
    void Destroy()
    {
        delete this;
    }

private:
    ~COnlyHeap() {}
}

 (2):禁止在堆中创建对象:隐藏掉 operator new() && operator delete()