Effective C++ 读书笔记【for_wind】

这边好书应该早点看的。总结一下,方便日后查阅。//for_wind

1、尽量以const,enum,inline替换#define,或说尽量以编译器取代预处理器
    #define可能并不进入符号表(symbol table)
    const:注意:A、常量指针;B、class专属常量。注:为了确保class专属常量至多只有一份实体,必须让它成为static成员。

2、尽量以<iostream>代替<stdio.h>

3、尽量以new和delete取代malloc和free
    差别在构造函数、析构函数

4、尽量以C++风格的注释形式
    即尽量用//,避免采用/* */在内嵌注释时,造成注释块过早结束

5、使用相同形式的new和delete
A、指的是new,delete和new[],delete[]配对使用。
B、当你使用new动态生成一个对象,有两件事发生:内存被分配;针对此内存会有一个(或更多)构造函数被调用,然后内存才被释放(delete).
C、尽量不要对数组形式做typedef动作。
      总结:当你用new生成对象时,如果用new type-object[] ,则要使用 delete []type-object ,否则使用 delete

6、记得在destructor中以delete对付pointer members
A、当存在pointer members时,
    每个constructor中将该指针初始化或或为0;
    在assignment运算符中,将该指针原有的内存删除,重新配置一块;
    在destructor中删除这个指针。
B、谁new,谁delete。
C、注意到smart pointers。
    以独立语句将newed对象置入智能指针 Store newed objects in smart pointers in standalone statements.
    以独立语句将newed对象存储于(置入)智能指针内。如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄露。

7、为内存不足的状况预先做准备
    set_new_handler。

8、撰写operator new 和 operator delete时应遵循的公约
    指需要和缺省的operator new 保持一致(正确的返回值,内存不足时调用错误处理函数,准备应付“no memory”的需求(申请0内存)(实际上视申请0bytes为1bytes));
    需要和 operator delete保持一致(保证删除一个null指针是安全的)。
    更具体地说,operator new 应该内含一个无穷循环,并在其中尝试分配内存,如果它无法满足内存需求,就该调用new-handler。它也应该有能力处理 0 bytes申请。Class专属版本则还应该处理“比正确大小更大的(错误)申请”。
    operator delete应该在收到null指针时不做任何事,Class专属版本则还应该处理“比正确大小更大的(错误)申请”。

9、避免遮掩了new的正规形式
    和8条一样,需要定义class专属的operater new并调用缺省的operater new

10、如果你写了一个operator new,请对应写一个operator delete。
    将这两个一并写出,使它们能够共享相同的假设。(重载它们大多是为了效率,一般可采用链表之类的POOL来管理)

11、如果class内动态配置有内存,请为此class声明一个copy constructor 和一个assignment运算符
    避免:内存泄露问题和指针别名问题(如,重复删除以及未定义问题)。
    如果不想用或没必要,不如:将这些函数声明为private,并且不要定义(实现)之,这可以阻止clients调用它们,也可以避免编译器产生它们。

12、在constructor中尽量以initiation动作(即member initiation list)取代assignment动作
    好处:A、满足const members 和 reference members必需通过member initiation list初始化;B、提高data members初始化的效率(减少函数调用)

13、initialization list中的members初始化次序应该和其在class内的声明次序相同
    注意到:class members是以它们在class内的声明次序来初始化的,而和member initialization list中出现的次序完全无关。
    (只有nonstatic data members的初始化才使用这条规则。)

14、总是让base class拥有virtual destructor。
    避免 nonvirtual destructor产生的“未定义行为问题”。
C++明确指出,当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义----实际执行时通常发生的是对象的derived 成分没被销毁。
    方法:给base classes 定义一个 virtual 析构函数。
任何class只要带有virtual 函数都几乎确定应该有一个virtual析构函数。
    欲实现出virtual函数,对象必须携带某些信息,主要用来在运行期决定哪一个virtual函数被调用。这份信息通常由一个所谓vptr(virtual table pointer)指针指出。
    令class带一个pure virtual(纯虚)析构函数会导致abstract(抽象)classes ---也就是不能被实体化(instantiated)的class.
总结:
    Polymorphic(带多态性质的)base classes 应该声明一个 virtual析构函数。如果 class带有任何virtual函数,它就应该拥有一个virtual析构函数。
    Classes 的设计目的如果不是作为base classes使用,或不是为了具备多态性(polymorphic),就不该声明Virtual析构函数。

15、令operator = 传回“ *this 的reference”
    形式:C&  C::operator=(const C&)
    {     .......... 
        return *this;
    }
    assignment运算符总是必须传回一个reference,指向其左侧引数,即*this。否则会妨碍assignment串链或(和)妨碍调用端的隐式型别转换。

16、在operator=中为所有data members设定(赋值)内容。
    当你编写一个copying 函数,请确保(1)复制所有local成员变量,(2)调用所有base classes内的适当的copying函数。
Copying 函数应该确保复制“对象内的所有成员变量”及“所有base class成员”。
    注意:Derived的copy constructor必须确保调用base 的copy constructor而不是base的default constructor。
    方法:在Derived copy constructor的成员初始列中为base指定初值。注意加粗的部分,不应该遗漏
    如:class Derived:public Base{
    public: 
          Derived(const Derived& rhs):  Base(rhs),y(rhs,y) { }
    };
不要尝试以某个copying函数实现另一个copying函数。应该将共同功能放进第三个函数中,并由两个copying函数共同调用。

17、在operator=中检查是否“自己赋值给自己”
    为了确保正确性和效率。
    别名问题(aliasing)和对象同等问题(object identity)出现的场合:不限于operator=函数内(只要出现references和pointers,任何两个代表兼容型别的对象名称都可能实际指向同一对象)。必须格外注意避免此类问题的发生。
    如何判断是否相等:通过值的比较或通过地址的比较,还可以设计并通过函数返回对象识别码。

18、努力让接口完满(complete)且最小化
    完成合理的工作;尽量少,不至于有重复重叠功能

19、区分member function,non-member function和friend function三者
    虚函数必须是class members;
    绝对不要让operator>>和operator<<成为members。应该设为non-member functions或friend(如果用到non-public members的话)。
    只有non-member functions 才能在其最左端引数(argument)身上实施型别转换。应该设为non-member functions或friend(如果用到non-public members的话)。
      上述情况外,设计成member function。
    注:宁可拿non-member non-friend函数替换member函数。这样做可以增加封装性、包裹弹性和技能扩充。
如果你需要为某个函数的所有参数(包括被this指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是个non-member。

20、避免将data members放在公开接口中
      不如设计成函数,从而获得“一致性”和优良的存取控制,(利用“函数抽象性”)。

21、尽可能使用const
A、如果关键字const出现在星号左边,表示被指类型是常量;
    如果出现在星号右边,表示指针自身是常量;
    如果在星号两边,表示被指类型和指针两者都是常量。
B、Const最具有代表性的是函数声明时的应用。Const可以和函数返回值、各参数、函数自身产生关联。
C、Const 成员函数:
    1)它们使class接口比较容易理解。
    2)它们使“操作const对象”成为可能。
D、如果函数的返回类型是个内置类型,那么修改函数返回值不合法。
E、Bitwise constness(physical constness)
    成员函数只有在不更改对象的任何成员变量(static除外)时才可以说是const。也就是说它不更改对象内任何一个位(bit)。
F、Logical constness
    一个const成员函数可以修改它所处理的对象内某些bits,但只有在客户端侦测不出的情况下才可以。
    Mutable(可变的)关键字可以释放掉non-static成员变量的bitwise constness约束;
G、在const和non-const成员函数中避免重复,如果可以,让后者调用前者
      Const成员函数调用non-const成员函数是一种错误行为,因为对象有可能因此被改动。

22、尽量使用pass-by-reference(传址),少用pass-by-value(传值)
      尽量以pass by reference 替换 pass by value 。前者通常比较高效,并可避免 切割问题(slicing problem)。
      不过并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言 pass by value 往往比较适当。
      如果你有个对象属于内置类型(例如 int),pass by value 往往比 pass by reference的效率高些。
      此外,pass by reference 你需要格外注意避免别名问题。

23、当你必须传回object,不要尝试传回reference
      牢记:reference是一个既有对象的名称。注意可能产生的内存泄露问题。

24、在函数重载和参数缺省化之间谨慎选择
考虑:A、是否有恰当的值可以用来当作缺省参数;B、有多少个算法。

25、避免对指针型别和数值型别进行重载
模凌两可。

26、防卫潜伏的ambiguity状态
“存取限制”不能解除“因多继承而来的members”的模凌两可状态。(理由:改变某个class member的可存取性绝不应该连带改变程序的意义。)

27、如果不想使用编译器暗自产生的member functions,就应该明白地拒绝它。
private,空。
可以自定义编译器会默认生成的函数,手动定义成private;或者使用空基类(empty base class)声明空函数来继承。

28、尝试切割global namespace
using

29、避免传回内部数据的handles
问题:违反抽象性,出现dangling handles和别名问题。

30、避免写出member functions,传回一个non-const pointer 或 reference并以之指向较低存取层级的members
问题:破坏存取保护,易出现别名问题。

31、千万不要传回“函数内local对象的reference”或“函数内以new获得的指针所指的对象”
问题:未定义行为,内存泄露

32、尽可能延缓变量定义式的出现
即需要时才定义,避免构造(和析构)非必要的对象,还可以避免无意义的default constructors。此外,可增加程序的清晰度并改善程序效率。

33、明智地使用inlining
A、inline,只是对编译器的一种提示,并 非是强制命令。意味着,在编译阶段将调用动作以被调用函数的本体取代之,是否真正inline,视编译器而定。
大部分的编译器会 拒绝复杂的(也就是内含循环或递归调用的)函数inline化,而所有(除了最平凡、几乎什么也没做的)的 虚拟函数都会阻止inlining的进行。
B、权衡调用函数的成本,和程序代码体积增加的成本。
好处:inline函数的函数体很小,较小的目标代码和较高cache命中率;
代价:免除函数调用的成本,但可能会增加目标代码的大小。inline行为所造成的程序代码膨胀会导致病态的换页行为(thrashing现象)。
inline函数无法随着程序库的升级而升级;如果函数有static对象,有反直观的行为,因此通常避免将其声明为inline;此外大部分除错器对inline函数束手无策
C、将大多数inlining限制在 小型、被频繁调用的函数上。这可使日后的调试过程和二进制 升级(binary upgradability)更容易,也可使潜在的代码膨胀问题最小化,使程序的速度提升机会最大化。不要只因为function templates出现在头文件,就将它们声明为inline。

34、将文件之间的编译依赖关系降至最低
支持“编译依存性最小化”的一般构想是:依赖于声明式,不要依赖于定义式。基于此构想的两个手段是Handle classes和Interface classes。
程序库头文件应该以“完全且仅有声明式”(full and declearation-only forms)的形式存在。这种做法不论是否涉及templates都适用

35、确定你的public inheritance,模塑出“is a”的关系
C++进行(OOP)面向对象编程,最重要的一个规则是:public inheritance (公开继承)意味“is - a”(是一种)关系。
如果你令class D(“derived”)以public形式继承class B(“Base”),你便是告诉编译器: 每一个类型为D的对象同时也是一个类型为B的对象,反之不成立。
“pubilc继承”意味is-a。适用于Base classes身上的每一件事情一定也是用于 Derived classes 身上,因为每一个Derived class对象也都是一个Base Class对象。


36、区分接口继承和实现继承 differentiate between inheritance of interface and  inheritance of implementation

      纯虚函数、一般(非纯)虚函数、非虚函数之间的差别,允许你精确地指定你希望derived class继承的东西:只继承接口,或是继承接口和缺省行为,或是继承接口和一份实现代码。
接口继承和实现继承不同。在pubilc继承下,derived classes总是继承Base class 的接口。
Pure virtual函数只具体指定接口继承。
Impure virtual 函数具体指定接口继承及缺省实现继承。
Non-virtual函数具体指定接口继承以及强制性实现继承。

37、绝不要重新定义继承而来的非虚拟函数。

38、绝对不要重新定义继承而来的缺省参数值。
      因为虚拟函数(你唯一应该复写的东西)是动态绑定(取决于指针或引用指向的类型),而缺省参数值却是静态绑定(取决与指针或引用的静态类型)。

39、避免在继承体系中做向下转型
      向下转型downcast:从一个“base class 指针”转为一个“derived class 指针”。问题:代码难以理解,且难以维护。解决方法:将转型动作以虚拟函数的调用取代,并且让每一个虚拟函数有一个无任何动作的缺省实现代码,以便应用在并不想要实行该虚拟函数的任何classes升上。

40、通过laying技术来模塑 has-a 或 is-implemented-in-terms-of的关系
     复合(composition)是类型之间的一种关系,当某种类型的对象内含它种类型的对象,便是这种关系。

       注意区别两者的关系。复用。

41、区分inheritance 和 templates
      inheritance用来产生一群classes,其中对象型别会影响class 的函数属性。
      templates用来产生一群classes,其中对象型别不会影响class的函数属性。

42、明智地运用private inheritance
如果classes之间的继承关系是private,编译器不会自动将一个derived class对象转换为一个Base class对象。
Private base class继承而来的所有成员,在derived class中都会变成private属性。

43、明智地使用多继承
     多继承引发的问题:模凌两可ambiguous。
     多重继承比单一继承复杂。它可能导致新的歧义性,以及对virtual继承的需要。
     Virtual继承会增加大小、速度、初始化(及赋值)复杂度等等成本。
    解决:可以尝试共同继承一个新加的公共的base class。
    多重继承的确有正当用途。其中一个情节涉及“public继承某个Interface class”和“private继承某个协助实现的class”的两相结合

44、说出你的意思并了解你所说的每一句话
    共同的base class意味着共同的特性。如果class D1和D2都声明class B为base,则D1和D2从B继承了共同的data members和member functions。
    public inheritance(公开继承)意味着“是一种(isa)”。如果class D以public方式继承class B,则每个D对象便是一个B对象,反之并不成立。
       private inheritance(私有继承)意味着“根据某物实现(is-implemented-in-terms-of)”。如果class D以private方式继承了class B,D对象便是根据D实现的;B对象和D对象之间并没有任何概念上的关系。
    Layering意味着“有一个(has-a)”或“根据某物实现(is-implemented-in-terms-of)”。如果class A内含一个型别为B的data member,那么A对象之中便有一个型别为B的成分,或者说A对象是根据B对象实现。
    ----只有当牵涉到public inheritance时,一下数点才成立:---见36.
    纯虚函数意味着:只有其函数接口会被继承。如果class C声明了一个纯虚函数mf,则C的subclasses必须继承mf的接口,而C的具象subclasses必须提供自己的实现代码。
    一般(非纯)虚函数意味着:函数接口及缺省实现代码都被继承。如果class C声明了一个一般(非纯)虚函数mf,C的subclasses必须继承mf的接口,它们可以继承mf的缺省实现代码(如果它们决定这么做的话)。
    非虚函数意味着:此函数的接口和其实现代码都会被继承。如果class C声明了一个非虚函数mf,那么C的subclasses必须同时继承mf的接口和实现代码。事实上可以说,mf为C定义了一个“不变性凌驾于变异性之上”的性质。

45、了解C++默默编写并调用哪些函数
C++会为默认的空类(empty class)添加
Default  constructor默认构造函数
Copy constructor构造函数
destructor 析构函数
assignment 赋值运算符
和一对address-of地址取值运算符
注:唯有这些函数被调用时,它们才会被编译器创建出来。
对于assignment 赋值运算符,以下情况拒绝产生一个缺省的operator=,你必须自行定一个assignment运算符。
A、“内含reference member”;B、内含const members;C、base class中将assignment运算符声明为private。

46、宁愿编译和连接时出错,也不要执行时才错

47、使用non-local static objects之前线确定它已有初值
采用Singleton pattern技术:以函数内static objects取代non-local static objects。在此函数调用期第一次遇到此对象的定义时,进行初始化。

48、不要对编译器的警告信息视如不见。

49、尽量让自己熟悉C++标准库

50、加强自己对C++的理解



以下是第三版中新加的,理解一下。“??”处没有完全理解

--21 more--
Const_cast
用法:const_cast<type_id>(expression)
该运算符用来修改类型的const或volatile属性。 
常量指针被转化成非常量指针,并且仍然指向原来的对象;
常量引用被转换成非常量引用,并且仍然指向原来的对象;
常量对象被转换成非常量对象;
Static_cast
 用法:static_cast<type_id>(expression)
该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。
C++ primer 里说明在进行隐式类型转换都用
Int I = Static_cast<int>f;  // float f = 1.42f; 


---
条款04:确定对象被使用前已被初始化 Make sure that object are initialized befor they’re used.

读取未初始化的值会导致不明确的行为。

对象的初始化何时一定发生,何时不一定发生。
对于无任何成员的内置类型,必须手工完成此事。
对于内置类型以外的任何其他东西,初始化责任落在构造函数(constructors)身上。确保每一个构造函数都将对象的每一个成员初始化。
C++规定, 对象的成员变量的初始化动作发生在进入构造函数本体之前
构造函数的最佳写法是, 使用 member initialization list(成员初始化表)如:
ABEntry::ABEntry(char &name,char& address,list &phones)
 : theName(name),theAddress(address),thePhones(phones)
{
……. 
}
编译器会为用户自定义类型(user-defined types)之成员变量自动调用default构造函数 --- 如果那些成员变量在“成员初始化列表”中没有被指定初值的话。
成员变量是 const 或 references,它们就一定需要初值,不能被赋值
C++有着十分固定的“成员初始化次序”。Base classes 更早于其derived classes 被初始化,而class的成员变量总是以其声明次序被初始化。
Static 对象,其有效时间从被构造出来直到程序结束为止,因此stack和heap-based对象被排除。
  C++对于“定义于不同的编译单元内的non-local static对象”的初始化相对次序并无明确定义。
    小方法:将每一个non-local static对象放到自己的专属函数内(该对象在此函数内被声明为static),这些函数返回一个reference指向它所含的对象。
总结:
为内置型对象进行手工初始化,因为C++不保证初始化它们。
构造函数最好使用成员初始化列表(member initialization list),而不要再构造函数本体内使用赋值操作(assignment).
为免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象。

-------
条款09:绝不在构造和析构过程中调用Virtual函数
Never call  virtual functions during construction or destruction
Base class构造期间 virtual 函数绝不会下降到 derived classes阶层。
(在base class构造期间,virtual函数不是virtual函数)


---
条款13:以对象管理资源 Use object to manage resources.

把资源放进对象内,我们便可依赖C++的“析构函数自动调用机制”确保资源被释放。
STL标准程序库提供的auto_ptr正是针对这种形式而设计的特制产品。Auto_ptr是个“类指针(pointer-like)对象”,也就是“智能指针”,其析构函数自动对其所指对象调用delete。
Std::auto_ptr<Investment>pInv(CreateInvesment());

上面的例子示范“以对象管理资源”的两个关键想法:
获得资源后立刻放进管理对象(managing object)内。
管理对象(managing object)运用析构函数确保资源被释放。
使用auto_ptrs有一个性质:若通过copy构造函数或copy assignment操作符复制它们,它们会变成NULL,而复制所得的指针将取得资源的唯一拥有权!

Auto_ptr的替代方案是“引用计数型智慧指针”(reference-counting smart pointer;RCSP)。RCSP也是个智能指针,持续追踪共有多少对象指向某笔资源,并在无人指向它时自动删除该资源。
TR1的tr1::shared_ptr就是个RCSP,你可以这么写
Std::tr1::shared_ptr<Investment>pInv(CreateInvestment());

Auto_ptr 和 shared_ptr 都是在其析构函数上调用delete ,而不是 delete []动作。那意味着在动态分配而得的array上使用,是错误的。
总结:
为防止资源泄露,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源。
两个常被使用的RAII classes 分别是tr1:: shared_ptr 和 auto_ptr

----------------
条款14:在资源管理类中小心copying行为
Think carefully about copying behavior in resource-managing classes.

RAII守则:资源在构造期获得,在析构期被释放;

类似Mutex的互斥对象(mutex object)时,因为有lock,unlock两种状态,可以采用以下方法,确保释放;
A、禁止复制。Auto_ptr创建
B、引用计数(reference-count)。Shared_ptr创建

总结:
复制RAII对象必须一并复制它所管理的资源,所以资源的copying 行为决定RAII对象的copying行为。
普遍而常见的RAII class copying行为是:抑制copying、施行引用计数法(reference counting)。不过其他行为也都可能被实现。
-----------------
条款15:在资源管理类中提供对原始资源的访问
Provide access to raw resources in resourece-managing classes

所有智能指针,如tr1::shared_ptr 和 auto_ptr 也重载了指针取值(pointer dereferencing)操作符(operator->)和(operator*),它们允许隐式转换至底部原始指针
总结:
API 往往要求访问原始资源(raw resources),所有每一个RAII class应该提供一个“取得其所管理之资源”的办法。
对原始资源的访问可能经由显示转换或隐式转换。一般而言显示转换比较安全,但隐式转换对客户比较方便。
-----------
条款17:以独立语句将newed对象置入智能指针
Store newed objects in smart pointers in standalone statements.
以独立语句将newed对象存储于(置入)智能指针内。如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄露。


----------
条款19:设计 class 犹如设计 type
Treat class design as type design.
设计高效类型(types)类(classes)的方法:
新type的对象应该如何被创建和销毁?
(构造和析构函数以及内存分配和释放)
对象的初始化和对象的赋值该有什么样的差别?
(构造函数和赋值(assignment)操作符的行为)
新type的对象如果被passed by value (以值传递),意味着什么?
(Copy函数用来定义一个type的passed by value)
什么是新type 的“合法值”?
   <异常处理>
你的新type需要配合某个继承图系(inheritance graph)吗?
   <是否需要虚函数>
你的新type需要什么样的转换?
什么样的操作符和函数对此新type而言是合理的?
什么样的标准函数应该驳回?
谁该取用新type的成员?
什么是新type的“未声明接口”(undeclared interface)?
你的新type有多么一般化?
你真的需要一个新type吗?
Class 的设计就是type的设计。在定义一个新type之前,请确定你已经考虑过本条款覆盖的所有主题。

-----------
条款27:尽量少做转型动作
Minimize casting.

Const_cast 通常被用来将对象的常量性转除(cast away the constness)。
Dynamic_cast用来执行“安全向下转型”,用来决定某对象是否归属继承体系中的某个类型。
Reinterpret_cast执行低级转型,取决于编译器,表示它不可移植。
Static_cast 强迫隐式转换。
如果可以尽量避免转型。
如果转型是必要的,试着将它隐藏于某个函数背后。
宁可使用C++style(新式)转型,不要使用旧式转型。
---------------
条款41:了解隐式接口和编译期多态
Understand implicit interfaces and compile-time polymorphism.

通常显示接口由 函数的签名式(函数名称、参数类型、返回类型)构成。
隐式接口并不基于函数签名式,而是由有效表达式(valid expressions)组成。

Classes 和 template 都支持接口(interfaces)和多态(polymorphism)。
classes而言接口是显示的(explicit),以函数签名为中心。多态则是通过 virtual函数发生于 运行期
template参数而言,接口是隐式的(implicit),奠基于有效表达式。多态则是通过 template具现化和函数重载解析(function overloading resolution)发生于 编译期
-----------
???条款42:了解typename的双重意义
Understand the two meanings of typename.

声明template参数时,不论使用关键字class或typename,意义完全相同。
请使用关键字typename标识嵌套从属类型名称;但不得在base class lists(基类列)或member initialization list(成员初始列表)内以它作为Base class修饰符。

------------------
????????条款43:学习处理模板化基类内的名称
Know how to access names in templatized base classes.

可在derived class templates 内通过“this->”指向base class templates内的成员名称,或藉由一个明白写出的“base class资格修饰符”完成。
--------------
条款44:将与参数无关的代码抽离templates
Factor parameter-independent code out of templates.

Templates 生成多个 classes 和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系。
因非类型模板参数(non-type template parameters)而造成的代码膨胀,往往可消除,做法是以函数参数或class成员变量替换template参数。
因类型参数(type parameters)而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述(binary representations)的具现类型(instantiation types)共享实现码。

??????条款45:运用成员函数模板接受所有兼容类型
Use member function templates to accept “all compatible types.”

使用member function templates(成员函数模板)生成“可接受所有兼容类型” 的函数。
如果你声明 member template 用于“泛化copy构造”或“泛化assignment操作”,你还是需要声明正常的copy构造函数和copy assignment操作符

??????条款46:需要类型转换时请为模板定义非成员函数
Define non-member functions inside templates when type conversions are desired.

当我们编写一个class template ,而它所提供的“与此template相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“class template内部的friend函数”。

??????条款48:认识template元编程
Be aware of template metaprogramming.

Template metaprogramming(TMP,模板元编程)可将工作由运行期移往编译器,因而得以实现早期错误侦测和更高的执行效率。
TMP可被用来生成“基于政策选择组合”(based on combinations of policy choices)的客户定制代码,也可用来避免生成对某些特殊类型并不适合的代码。

    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值