条款25:将 constructor和 non-member function 虚化
1️⃣ Virtual constructor virtual constructor听起来很荒谬,但是它们很有用。 2️⃣ 案例引入123456789101112131415161718192021class NLComponent { // 抽象基类,用于时事消息public: // 的组件(components), ... // 其中内含至少一个纯虚函数。};class TextBlock: public NLComponent {public: ... // 没有内含任何纯虚函数。};class Graphic: public NLComponent {public: ... // 没有内含任何纯虚函数。};class NewsLetter { //一份时事通信是由一系列的public: // NLComponent 对象构成的。 ...private: ...
条款24:了解 virtual function、multiple inheritance、virtual base class、runtime type identification 的成本
1️⃣ 问题的引入 当一个“虚函数”被调用时,执行的代码必须对应于“调用者(对象)的动态类型”。 对象的pointer或reference,其类型是无形的,那么编译器是如何有效率的实现这样的行为呢? 编译器采用的是“virtual table”和“virtual table pointer”。 2️⃣ 虚表(virtual table) 定义 vtbl通常是一个由“函数指针”架构而成的数组。 程序中的每一个class凡声明(或继承)虚函数者,都有自己的一个vtbl,而其中的条目就是该 class的各个虚函数实现体的指针。 123456789101112class C1{public: C1(); virtual ~C1() ; virtual void f1() ; virtual int f2(char c) const; virtual void f3(const string& s); void f4 () const; ...}; C1虚表示意图 ...
条款26:限制某个class所能产生的对象的数量
1️⃣ 允许零个或一个对象 定义 每当即将产生一个对象,我们确知一件事情:会有一个constructor被调用。 “阻止某个class产出对象”的最简单方法就是将其<font style="background-color:#FBDE28;">constructors</font>声明为<font style="background-color:#FBDE28;">private</font> 2️⃣ 问题的引入 假设我想为打印机设计一个class,我希望设下“只能存在一台打印机”的约束。 我们可以将”打印机对象“封装在某个函数内,如此一来每个人都能够取用打印机,但只有唯一一个打印机对象会被产生。 12345678910111213141516171819class PrintJob; //前置声明class Printer {public: void submitJob(const PrintJob& job); void reset (); ...
条款19:了解“临时对象”的来源
1️⃣ 什么是临时对象? 局部变量 在函数作用域定义的temp并非“临时对象”,而是“局部对象”。 1234567template< class T>void swap(T& object1, T& object2){ T temp = object1; object1 = object2; object2 = temp;} 临时对象 “临时对象”是不可见的——不会在你的源代码中出现。 只要产生一个non-heap object而没有为它命名,便诞生了一个“临时对象”。 2️⃣ 临时对象产生于什么情况? 发生隐式类型转换,以求函数调用能够成功。 当函数返回对象的时候。 3️⃣ 进一步理解“为了让函数调用成功”而产生的临时对象 产生时机 传递某对象给一个函数,而其类型与它即将绑定上去的参数类型不同。 代码解析 123456789101112//返回 ch 在str中的出现个数。size_t countChar(const string& str, char ch);char...
条款12:了解“抛出一个 exception”与“传递一个参数”或者“调用一个虚函数”之间的差异
1️⃣ 函数参数和catch语句的声明方法1class Widget {...}; //某个class,细节不重要。 123456789101112void f1 (Widget w) ; // 所有这些函数需要的参数void f2 (Widget& w) ; // 分别是Widget,Widget&void f3 (const Widget& w); // 或 Widget*类型。void f4 (Widget *pw) ;void f5(const Widget *pw);catch (Widget w) ... // 所有这些catch子句catch (Widget& w)... // 用来捕捉类型为catch (const Widget& w)... // Widget, Widget&或catch (Widget *pw) ... // Widget* 的 exceptions。catch (const Widget *pw)... 2️⃣...
条款8:了解各种不同意义的 new 和 delete
1️⃣ 问题的引入 如何理解new operator 和 opertor new 之间的差异 ? 什么是 placement new,它的作用是什么? 2️⃣ 案例解析1string* ps = new string("Memory Management"); 这里使用的new是所谓的new operator。这个操作符是由语言内建的,不能被改变意义。 它的动作以下两方面: 第一,它分配足够的内存,用来放置某类型的对象。 第二,它调用一个constructor,为刚才分配的内存中的那个对象设定初值。 我们能够改变的是用来容纳对象的那块内存的分配行为。 newo perator调用某个函数,执行必要的内存分配,我们可以重写那个函数,改变其行为。 这个函数的名称叫做operator new。 3️⃣ 进一步理解 函数operator new 的定义式: 1void* operator new(size_t size); - 返回值和参数解析 * 返回值类型为 `void*`,返回的指针,指向一块原始的、未设初值的内存 *...
条款11:禁止异常(exceptions)流出 destructor 之外
1️⃣ destructor 函数的调用时机 对象正常状态下被销毁,也就是当它离开了它的作用域(scope)或是被明确地删除。 当对象被exception处理机制销毁——也就是exception传播过程中的(stack-unwinding)机制。 2️⃣ 理由一 有个exception正撰写你的destructor。因为如果控制权基于exception的因素离开destructor,而此时正有另一个exception处于作用状态,C++会调用terminate函数。 12345678910class Session{public: Session(); ~Session();...private: static void logCreation(Session* objAddr); static void logDestruction(Session* objAddr);}; 函数 logCreation 和 logDestruction 分别用来记录对象的构造和析构。 ...
条款6:了解各种不同意义的 new 和 delete
1️⃣问题的引入 重载函数是以参数类型来区分彼此,然而不论 ++或 – 操作符的前置或后置式,都没有参数。 为了填平语言上的漏洞,只好让后置式有一个int自变量,并且在它被调用时,编译器默默为该int指定一个0值。 123456789101112131415class UPInt { // "unlimited precision int"public: UPInt& operator++(); //前置式(prefix)++。 const UPInt operator++(int); //后置式(postfix)++。 UPInt& operator--(); // 前置式(prefix)-—。 const UPInt operator--(int); //后置式(postfix)--。 UPInt& operator+=(int); //+=操作符,结合UPInts和intS。...};UPInt i;++i; //...
条款1:Pointer 和 Reference
1️⃣语言层面共同点 都是用来参考其他对象。 不同点 reference必须绑定到某个对象,不产生所谓的空引用null reference,也就是说 reference 在定义时必须要有初值;但是 pointer 可以指向一个对象,也可以不指向任何对象,在定义时可以没有初值。 reference比pointer 更富有效率。因reference在使用前不需要测试有效性,但是pointer 在使用前需要检验指针是否为空。 pointer 可以被重新赋值,指向另外一个对象;但是reference总是代表最初获得的对象。 123456789101112string s1("zhang san");string s2("li si");string& refer = s1; // refer 绑定 s1string* pointer = &s1; // pointer 指向 s2// 现在进行修改refer = s2; // refer 仍然绑定 s1,只不过对 refer 的操作实质是 ...
条款03:不以多态的性质处理数组
1️⃣ 继承 继承的最重要性质就是:可以通过“指向base class objects”的 pointers 或 references,来操作 derived class objects。如此的pointers和 references,我们说其行为是多态的。 2️⃣ 问题 C++允许你通过base class的 pointers和 references来操作“derived class objects“所形成的数组。 3️⃣ 例子12class BST { ... };class BalanceBST : BST { ... }; 123456void printBSTArray(ostream& s, const BST array[], int numElements){ for (int i = O; i < numElements; ++i) { s << array[i]; // 假设 BST objects 有一个 operator<< 可用。...