zoukankan      html  css  js  c++  java
  • Effective 笔记

                                                             

     

     

     

     

     

     

    1,C++属于一个语言联邦 : C     Object-Oriented C++     Template C++       STL

    2,应尽量以const,enum,inline替换#define

      如果一个常量是class专属常量又是static,且为整数类型(int,char,bool),则需特殊处理:只要不取它们的地址,可以声明并使用它们而不须提供定义式。

    但是如果取某个class专属常量的地址,或纵使不取其地址而编译器却(不正确的)坚持要看到一个定义式,必须提供定义式

    enum的行为某方面比较像#define而不像const:取一个const地址合法,但是取一个enum地址就不合法,而取#define也不合法,如果不想让被人获得一个pointer或reference指向你的某个整数常量,enum可以帮助你实现这个约束

    #define和enum不会设定额外的空间,const在优秀的编译器中也许可能也是这样,但是不够优秀的编译器就必须设定额外的空间

    3,尽可能使用const

    const语法虽然变化多端,但并不莫测高深。如果关键字const出现在*左侧,表示被指物是常量;如果出现在*右边,表示指针自身是常量;如果出现在*两边,表示被指物和指针都是常量

    std::vector<int>  vec;

    const std::vector<int>::interator  iter = vec.begin()                    T*const

    std::vector<int>:const_iterator iter = vec.begin()                       const T*

     

    将const实施于成员函数的目的,是为了确认该成员函数可作用于const对象身上

    const特性的两个函数可以重载(必须在类中,不在类中会出现问题) const函数不可改变对象任何变量(注意区分指针的情况,只要指针不变,指向的值可变化)

    注意操作符[]的重载要返回值的引用,否则不能对结果赋值(相当于对值的一个拷贝赋值,没有意义)

    mutable可以在常函数中修改其值,但是一般作用是:1,用于缓存2,或必须在常函数中修改值

     

    利用const operator[]实现出non-const版本:

    class TextBlock {

    public:

               const char & operator[](std::size_t position) const {  .....               return text[position]; }

               char & operator[](std::size_t position){

                      return  const_cast<char &>(  static_cast<const  TextBlock&> (*this)[position]);

    };

     

    非const调用const是安全的

    const调用非const是不安全的,(因为非const有可能改变)

    总结:

          1>将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体

          2>编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”(conceptual constness)

          3>当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复

    4,确定对象被使用之前已被初始化:

    为了保证一定性,使用内建数据类型一定要初始化,构造对象要保证构造函数初始化它的成员

    C++ 对“定义在不同的编译单元内的non-local  static对象”的初始化相对次序并无明确定义。因为决定它们初始化次序相当困难,根本无解

    最常用的形式,就是  多个编译单元内的non-local static对象经由“模板隐式具体化,implicit template instantiations"形式(而后者自己可能也是经由”模板隐式具现化“形成)

     

    任何一种non-const static对象,不论它是local或non-local,在多线程环境下“等待某事发生”都会有麻烦。处理方法:在程序的单线程启动阶段手工调用所有的reference-returning函数,这可消除与初始化有关的“竞速形势”

     

    为了避免在对象初始化之前过早地使用它们,你需要做三件事:

    第一,手工初始化内置型non-member对象。

    第二,使用成员初值列对付对象的所有成分

    第三,在”初始化次序不缺定性“(这对不同编译单元所定义的non-local static对象是一种折磨)氛围下加强设计

     

    总结:

                1>为内置对象进行手工初始化,因为C++不保证初始化它们

                2>构造函数最好使用成员初始化列表,而不要在构造函数体内使用赋值操作。次序和变量声明的顺序一致

                3>为免除“跨编译单元之初始化次序”问题,请以local  static对象替换non-local static对象

    5,编译器默认提供四个public且inline函数,赋值,拷贝,构造,析构,但是它们是在需要的时候才被编译器创建出来的

    C++不允许让引用改指向不同的对象

    >如果打算在一个“内含reference成员”的class内支持赋值操作,必须自己定义copy assignment操作符

    >如果某个base class将copy assignment操作符声明为private,编译器将拒绝为其derived classes 生成copy assignment操作符

     

    6,若不想使用编译器自动生成的函数,就该明确拒绝:将他们声明为private,同时不要实现他们,这样的话即使friend或者成员函数调用他们连接器会提示错误

    另外一种方法是把他们声明为private在一个单独的类中,然后用这个类继承它

    7,为多态基类声明virtual析构函数

    >带有多态性质的base classes应该声明一个virtual析构函数。如果class带有任何virtual函数,他就应该拥有一个virtual析构函数

    >classes的设计目的如果不是作为base classes使用,或不是为了具备多态性,就不该声明virtual析构函数

    8,别让异常逃离析构函数

    >析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序

    >如果客户需要对某个操作函数运行期间抛出异常作出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作

    9,绝不在构造和析构过程中调用virtual函数

    >无法使用virtual函数从base classes向下调用,在构造期间,可以藉由“令derived classes将必要的构造信息向上传递到base class构造函数”替换加以弥补

    >在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层)

    10,令operator=返回一个 reference to *this

                                                      目的是:为了实现“连锁赋值”

    11,在operator=中处理“自我赋值”

    自我赋值     发生在对象被赋值给自己时

    >确保当对象自我赋值时operator=有良好行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap

    >确定任何函数如果操作一个以上的对象,而其中多个对象是同一对象时,其行为仍然正确

    12,复制对象时勿忘其每一个成分

    自己编写copying函数,要确保:

    1,复制所有local成员变量

    2,调用所有base classes内的适当copying函数

     

    >Copying函数应该确保复制“对象内的所有成员变量”及“所有base class成分”

    >不要尝试以某个copying函数实现另一个copying函数。应该将共同机能放进第三个函数中,并由两个copying函数共同调用

     

    所谓资源就是:一旦用了它,将来必须还给系统,否则会出现问题。C++最常用的资源就是动态内存分配,当然还有其他资源:文件描述扶,互斥锁,图形界面中的字型和笔刷、数据库连接、以及网络sockets

    13,以对象管理资源

    两个关键想法:

    >获得资源后立刻放进管理对象内

    >管理对象运用析构函数确保资源被释放

     

    由于auto_ptr被销毁时会自动删除它所指之物,所以一定要注意别让多个auto_ptr同时指向同一对象。如果真是那样,对象会被删除一次以上,而那会使你的程序搭上驶向“未定义行为”的快速列车上。为了预防这个问题,auto_ptr有一个不寻常的性质:若通过copy构造函数或copy assignment操作符复制它们,它们会变成null,而复制所得的指针将取的资源的唯一控制权!!!(该性质有利也有弊)

     

    auto_ptr的替代方案是“引用计数型智慧指针”(RCSP),RCSP也是个智能指针,持续追踪共有多少对象指向某笔资源,并在无人指向它时自动删除该资源。RCSP提供的行为类似垃圾回收,不同的是RCSP无法打破环状引用:::tr1:shared_ptr就是一个RCSP

     

    注意:auto_ptr和tr1::shared_ptr两者都在析构函数内做delete而不是delete[]动作!!如果对数组指针调用则是馊主意

     

    >为了防止资源泄漏,请使用RAII对象,它们在构造函数中活得资源并在析构函数中释放资源 (Resource Acquisition Is Initialization,RAII)

    >两个常被使用的RAII  classes分别是tr1::shared_ptr和auto_ptr。前者通常是较佳选择,已经其copy行为比较直观。若选择auto_ptr,复制动作会时它(被复制物)指向null

    14,在资源管理类中小心copying行为

    在RAII中当一个对象被复制时,出现严重后果,一般有两种方案:

    >禁止复制(声明为private)

    >对底层资源祭出“引用计数法”(reference-count)           这种做法一般如果内含有一个tr1::shared_ptr ,但是默认事件是count为0时删除,幸运的是我们可以自己指定为0时的事件

     

    类的析构函数会自动调用其non-static成员变量的析构函数

    复制底部资源                             (深度拷贝)

    转移底部资源的拥有权              (auto_ptr)

     

    15,在资源管理类中提供对原始资源的访问

    tr1::shared_ptr和auto_ptr都提供了一个get函数,用来执行显式转换,也就是它返回智能指针内部的原始指针,以便于直接访问时可以通过

    它们还重载了(->和*)操作符

     

    >APIs往往要求访问原始资源(raw resources),所以每一个RAII class应该提供一个“取得其所管理之资源”的办法

    >对原始资源的访问可能经由显式转换或隐式转换。一般而言显式转换比较安全,但隐式转换对客户比较方便

     

    16,成对使用new和delete时要采用相同形式

               最好不要用typedef,否则会造成语义不清:

             typedef  std::string AddressLines[4];

             std::string  *pal = new AddressLines;

             delete pal;          错误!!!

             delete[] pal ;       正确!!!

    17,以独立语句将newed对象置入智能指针

    >      以独立语句将newed对象存储于(置入)智能指针内。如果不这样做,一旦异常被抛出,有可能导致难以觉察的资源泄露

     

    18,让接口容易被正确使用,不易被误用

    >好的接口很容易被正确的使用,不容易被误用。应该在所有的接口中努力达成这些性质

    >“促进正确使用”的办法包括接口一致性,以及与内置类型的行为兼容

    >“组织误用”的办法包括建立新类型、限制类型上的操作,束缚对象值,以及消除客户的资源管理责任

    >tr1::shared_ptr支持定制型删除器。这个防范DLL问题,可被用来自动解除互斥锁等等

     

    19,设计class犹如设计type

    好的types是一项艰巨的工作。好的types有自然的语法,直观的语义,以及一或多个高效的实现品

    如何设计好的classes:

    >新的type的对象应该如何被创建和销毁

    >对象的初始化和对象的赋值该有什么差别

    >新的type的对象如果被passed by value(以值传递),意味着什么

    >什么是新type的“合法值”

    >新的type需要配合某个继承图系么

    >新的type需要什么样的转换

    >什么样的操作符和函数对此新type而言是合理的

    >什么样的标准函数应该驳回

    >谁该取用新type的成员

    >什么是新type的“未声明接口”

    >新type有多么一般化

    >真的需要一个新type么

     

    20,宁以pass-by-reference-to-const替换pass-by-value

    >尽量以pass-by-reference-to-const替换pass-by-value。前者通常比较高效,并可避免切割问题

    >以上规则并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言,pass-by-value往往比较适当

     

    21,必须返回对象时,别妄想返回其reference

    任何时候看到一个reference声明式,都应该立刻问自己,它的另一个名称是什么?因为它一定是某物的另一个名称

    任何函数如果返回一个reference指向某个local对象,都将一败糊涂

    >绝不要返回point或reference指向一个local stack对象,或返回reference指向一个heap-allocated对象,或返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象

     

    22,将成员变量声明为private

    >切记将成员函数声明为private。这可赋予客户访问数据的一致性、可细微划分访问控制、允许约束条件获得保证,并提供class作者以充分的实现弹性

    >protected并不比public更具封装性

     

    23,宁以non-menber、non-friend替换menber函数

    这样做可以增加封装性、包裹弹性和机能扩充性

     

    24,若所有参数皆需要类型转换,请为此采用non-member函数

    >如果需要为某个函数的所有参数(包括被this指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是个non-member

     

    25,考虑一个不抛出异常的swap函数

     pimpl手法    :point to implementation

    C++只允许对class template偏特化,在function templates身上偏特化行不通,解决方案是:为它添加一个重载版本

    >当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定 这个函数不抛出异常

    >如果你提供一个member swap,也该提供一个non-member swap用来调用前者。对于classes(而非templates),也请特化std::swap

    >调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”

    >为“用户定义类型”进行std  template全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西

     

    26,尽可能延后变量定义式的出现时间

     >尽可能延后变量定义式的出现。这样做可增加程序的清晰度并改善程序的效率。

     

    27,尽量少做转型动作

    >const_cast通常被用来将对象的常量性转除。它也是唯一有此能力的C++-style转型操作符

    >dynamic_cast主要用来执行“安全向下转型”,也就是用来决定某对象是否归属继承体系中的某个对象。它是唯一无法由旧式语法执行的动作,也是唯一可能耗费重大运行成本的转型动作

    >reinterpret_cast意图执行低级转型,实际动作(及结果)可能取决于编译器,这也就是表示它不可移植。例如将point to int转型为int

    >static_cast用来强迫饮食转换。例如将non-const转型const对象,或将int转型double等等。但是无法将const转型non-const

     

    任何一个类型转换(不论是通过转型操作而进行的显式转换,或通过编译器完成的隐式转换)往往真的令编译器编译出运行期执行的码

     

    单一对象(例如Derived对象)可能拥有一个以上的地址(例如“以base *指向它”时的地址和“以Derived *指向它”时的地址)行为

    之所以需要dynamic_cast,通常是因为想在一个认定为derived对象身上执行derived class操作函数,但手上却只有一个"指向base”的pointer或reference,只能靠它们呢来处理对象

    绝对必须避免的一件事是所谓的"连串 dynamic_casts"

     

    >如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_casts。如果有个设计需要转型这个动作,试着发展无需转型的替代品

    >如果转型是必要的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需要将转型放进他们自己的代码内

    >宁可使用C++-style转型,不要使用旧式转型。前者容易辨识出来,而且也比较有着分门别类的职掌

     

    28,避免返回handles指向对象内部成分

     两个教训:

              --->成员变量的封装性最多只等于"返回其reference"的函数的访问级别

              --->如果const成员函数传出一个reference,后者所指数据与对象自身关联,而它又被存储于对象之外,那么这个函数的调用者可以修改那笔数据

    handles: 指针,reference,迭代器

    避免返回handles(reference,pointer,iterator)指向对象内部。遵守这个约定可增加封装性,帮助const成员函数的行为像个const,并将发生”虚吊号码牌“(dangling handles)的可能性降到最低

     

    29,为"异常安全"而努力是值得的

    当异常被抛出时,带有异常安全性的函数会:

    =>不泄露任何资源

    =>不允许数据败坏

     

    异常安全函数提供以下三个保证之一:

    ==>基本承诺

    ==>强烈保证

    ==>不抛掷保证

    1,> 异常安全函数(Exception-safe functions)即使发生异常也不会泄露资源或允许任何数据结构败坏。这样的函数区分三种可能的保证:基本型,强烈型,不抛异常型

    2,>“强烈保证"往往能够以copy-and-swap实现出来,但"强烈保证"并非对所有函数都可实现或具备实现意义

    3,>函数提供的"异常安全保证"通常最高只等于其所调用之各个函数的"异常安全保证"中的最弱者

    30,透彻了解inlining的里里外外

    inline只是对编译器的一个申请,不是强制命令

    inline函数通常 一定 被置于头文件内

    inline 在大多数C++程序中时编译器行为,只有少数基于.NET CLI(Common Language Infrastructure,公共语言基础设施)可以在运行期完成inlining

    Template通常也被置于头文件中,以为一旦被使用,编译器为了将它具现化,需要知道它长什么样子(像inline),(并不是同一准则,某些建置环境可以在连接期执行template具现化)

    编译器拒绝将过于复杂的函数(循环或递归)的函数inlining。而所有对virtual函数的调用(除非最平淡无奇的)也对会使inling落空,因为virtual是运行期确定

    编译器通常不对“通过函数指针而进行的调用”实施inlining,因为要确定地址

    inline函数无法升级

    很多调试器对inline函数束手无策

     

    >将大多数inlining限制在小型、被频繁调用的函数身上。这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化,使程序的速度提升机会最大化

    >不要只因为function templates出现在头文件,就将它们声明为inline

    31,将文件间的编译依存关系降至最低

    如果使用object references 或 object pointers可以完成任务,就不要使用objects

    如果能够,尽量以class声明替换class定义式

    为声明式和定义式提供不同的头文件

    >支持“编译依存性最小化”的一般构想是:相依于声明式,不要 相依于定义式。基于此构想的两个手段是Handle classes和Interface classes

    >程序库头文件应该以“完全且仅有声明式”的形式存在。这种做法不论是否涉及templates都适用

    继承与面向对象

    32,确定public继承塑模出is-a关系

    以C++进行面向对象编程,最要的一个原则:公开继承 意味 "is-a“的关系

    企鹅是一种鸟,但企鹅不会飞 理论解决办法:1,双继承  2,重新(一个错误的)飞功能  3,都不写飞功能

    >"public"继承意味着is-a。适用于base classes身上的每一件事情一定也适用于derived classes身上,因为每一个derived class对象也都是一个

    base class对象

    33,避免遮掩继承而来的名称

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class Base {
     5 private:
     6     int x;
     7 public:
     8     virtual void mf1() = 0;
     9     virtual void mf1(int);
    10     virtual void mf2();
    11     void mf3();
    12     void mf3(double);
    13 };
    14 
    15 class Derived : public Base {
    16 public:
    17     virtual void mf1();
    18     void mf3();
    19     void mf4();
    20 };
    21 
    22 int main()
    23 {
    24     Derived d;
    25     int x;
    26 
    27     d.mf1();   //Derived::mf1
    28     d.mf1(x);  //ERROR !!!  NO ARGS              using Base::mf1
    29     d.mf2();   //Base::mf2
    30     d.mf3();   //Derived::mf3
    31     d.mf3(x);  //ERROR !!! NO ARGS               using Base::mf3
    32 
    33     return 0;
    34 }

     上面的程序只是为了说明编译时候的正确性,因此有些函数没有实现,链接肯定通不过,不过能说明问题

    >derived classes内的名称会遮掩base classes内的名称。在public继承下从来没有人希望如此

    >为了让被遮掩的名称再见天日,可使用using声明式或转交函数

    34,区分接口继承和实现继承

     声明一个pure virtual函数的目的是为了让derived classes只继承函数接口

     声明简朴的(非纯)impure virtual函数的目的,是让derived classes继承该函数的接口和缺省实现

     声明non-virtual函数的目的是为了令derived classes继承函数的接口及一份强制性实现

     一个典型的程序有80%的执行时间花费在%20的代码身上

    >接口继承和实现继承不同。在public继承之下,derived classes总是继承base class的接口

    >pure virtual函数只具体指定接口继承

    >简朴的(非纯)impure virtual函数具体指定接口继承及缺省实现继承

    >non-virtual函数具体指定接口继承以及强制性实现继承

    35,考虑virutal 函数以外的其他选择

    >virtual函数的替代方案包括NVI手法及Stratrgy设计模式的多种形式。NVI手法自身是一个特殊形式的Template Method设计模式

    >将机能从成员函数移到class外部函数,带来的一个缺点是,非成员函数无法访问class的non-public成员

    >tr1::function对象的行为就像一般函数指针。这样的对象可接纳“与给定值目标签名兼容”的所有可调用物

    36,绝不重新定义继承而来的non-virtual函数

    >绝对不要重新定义继承而来的non-virtual

    37,绝不重新定义继承而来的缺省参数值

    virtual函数系动态绑定,而缺省参数值却是静态绑定

    >绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定,而virtual函数—你唯一应该覆写的东西——确实动态绑定

     

    38,通过复合塑模出has-a或“根据某物实现出”

    >复合的意义和public继承完全不同

    >在应用域,复合意味has-a。在实现域,复合意味is-implemented-in-terms-of(根据某物实现出)

    39,明智而审慎的使用private继承

    private继承主要用于“当一个意欲成为derived class者想访问一个意欲成为base class者的protected成分,或为了重新定义一或多个virtual函数

    EBO  empty base optimization 空白基类最优化,只适用于单一继承

    复合和private继承都意味 is-implemented-in-terms-of

    >Private 继承意味is-implemented-in-terms-of(根据某物实现出)。它通常比复合的级别低。但是当derived class需要访问protected base class的成员,或需要重新定义继承而来的virtual函数时,这么设计是合理的

    >和复合不同,private继承可以造成empty base最优化。这对致力于“对象尺寸最小化”的程序库开发者而言,可能很重要

    40,明智而审慎的使用多重继承

    C++编译器解析重载函数调用的规则:在看到是否有个函数可取用之前,C++首先确认这个函数对此调用的最佳匹配。找出最佳匹配函数后才检验其可取用性

    virtual base的初始化责任是由继承体系中的最低层class 负责。这暗示:

    1>classes若派生自virtual bases而需要初始化,必须认知其virtual bases——不论那些bases距离多远

    2>当一个新的derived class加入继承体系中,它必须承担起virtual bases(不论直接还是间接)的初始化责任

    对待virtual base classes: 1,非必须不是有2,如果必须使用,尽可能避免在其中放置数据

    >多重继承比单一继承复杂。它可能导致新的歧义性,以及对virtual继承的需要

    >virtual继承会增加大小,速度,初始化(及赋值)复杂度等等成本。如果virtual base classes不带任何数据,将是最具实用价值的情况

    >多重继承的确有正当用途。其中一个情节涉及"public继承某个Interface class"和"private 继承某个协助实现的class"的两相组合

    41,了解隐式接口和编译期多态

    Templates及泛型编程的世界,与面向对象有根本的不同。在此世界中显式接口和编译期多态依然存在,但重要性降低。反倒是隐式接口和编译期多态移到前面了

    >classes和templates都支持接口和多态

    >对classes而言接口是显式的,以函数签名为中心。多态则是通过virtual函数发生于运行期

    >对template参数而言,接口是隐式的,奠基于有效表达式。多态则是通过template具现化和函数重载解析发生于编译期

    42,了解typename的双重意义

    template内出现的名称如果相依于某个template参数,称之为从属名称

    如果C++解析器在template中遭遇一个嵌套从属名称,它便假设这个名称不是个类型,除非告诉它是(typename)

    typename不可以出现在base classes list内的嵌套从属类型名称之前,也不可以在member initialization list中作为base class修饰符

    >声明template参数时,前缀关键字class和typename可互换

    >请使用关键字typename标识嵌套从属类型名称;但不得在base class list(基类列)或member initialization list内以它作为base class修饰符

    43, 学习处理模板化基类内的名称

    class 定义式最前面的"template<>"语法象征这既不是template也不是标准的class 。而是模板全特化

    C++编译期往往拒绝在templatized base classes(模板化基类)内寻找继承而来的名称,因为有可能特化而拥有不同的接口!可这是个严重的问题,我们可以有三个办法解决:

    1,在调用base class函数动作之前加上this->

    2, 使用using声明式

    3, 明白指出被调用函数位于base class内,但是如果是虚函数的话会导致关闭虚函数"virtual 绑定行为"

    >可在derived class tempalte内通过"this->"指涉base class templates内的成员名称,或藉由一个明白的"base class资格修饰符"完成

    44, 将于参数无关的代码抽离templates

    >Template生成多个classses和多个函数,所以任何template代码都不该与某个构造成膨胀的template参数产生相依关系

    >因非类型模板参数而造成的代码膨胀,往往可消除,做法是以函数参数或class成员变量替换template参数

    >因类型参数而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述的具现类型共享实现码

    45,运用成员函数模板接受所有兼容类型

    >请使用member function templates生成"可接受所有兼容类型"的函数

    >如果你声明member templates用于"泛化copy构造"或"饭或assignment操作",你还是需要声明正常的copy构造函数和copy assignment操作符

    46, 需要类型转换时请为模板定义非成员函数

    在template实参推导过程中从不将隐式类型转换函数纳入考虑

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

    47, 请使用traits classes表现类型信息

    >Traits classes使得"类型相关信息"在编译期可用。它们以templates和"templates特化"完成实现

    >整合重载技术后,traits classes有可能在编译期对类型执行if ... else 测试

    48, 认识template元编程

     1 #include <iostream>
     2 
     3 template<unsigned n>
     4 struct Factorial
     5 {
     6     enum { value = n * Factorial<n-1>::value};
     7 };
     8 
     9 template<>
    10 struct Factorial<0> {
    11     enum { value = 1 };
    12 };
    13 
    14 
    15 int main(int argc,char *argv[])
    16 {
    17     std::cout << Factorial<5>::value << std::endl;
    18     std::cout << Factorial<10>::value << std::endl;
    19 
    20     return 0;
    21 }


    好处:1,确保量度单位正确 2,优化矩阵 3, 可以生成客户定制之设计模式

    >Template metaprograming可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的执行效率

    >TMP可被用来生成"基于政策选择组合"的客户定制代码,也可用来避免生成对某些特殊类型不适合的代码

    49,了解new-handler的行为

    new-handler函数必须做以下事情:

    1= 让更多内存可被使用

    2= 安装另一个new-handler

    3= 卸除new-handler

    4= 抛出bad_alloc(或派生自bad_alloc)的异常

    5= 不返回

    operator new要做以下事情:

    1= 调用set_new_handler,告知Widget的错误处理函数。这会将class的new-handler安装为global new-handler

    2= 调用global operator new,执行实际之内存分配。如果分配失败,global operator new会调用class的new-handler,因为那个函数才刚被安装为global new-handler。如果global operator new最终无法分配足够内存,会抛出一个bad_alloc异常。在此情况下class的operator new必须恢复原来的global new-handler,然后再传播该异常。为确保原来的new-handler总是能够被重新安装回去,class将global new-handler视为资源并遵守忠告,运用资源管理对象防止资源泄漏

    3= 如果global operator new能够分配足够一个class对象所用的内存,class的operator new会返回一个指针,指向分配所得。class析构函数会管理golbal new-handler,它会自动将class ‘s operator new被调用前的那个global new-handler恢复回来

    50,了解new和delete的合理替换机制

    51,编写new和delete时需固守常规

    52,写placement new 也要写 placement delete

    53,不要轻易忽略编译期警告

    54,让自己熟悉包括TR1在内的标准程序库

    55,让自己熟悉Boost

  • 相关阅读:
    Overloaded的方法是否可以改变返回值的类型
    parseXXX的用法
    java的类型转换问题。int a = 123456;short b = (short)a;System.out.println(b);为什么结果是-7616?
    UVA 10405 Longest Common Subsequence(简单DP)
    POJ 1001 Exponentiation(大数处理)
    POJ 2318 TOYS(计算几何)(二分)
    POJ 1265 Area (计算几何)(Pick定理)
    POJ 3371 Flesch Reading Ease (模拟题)
    POJ 3687 Labeling Balls(拓扑序列)
    POJ 1094 Sorting It All Out(拓扑序列)
  • 原文地址:https://www.cnblogs.com/lfsblack/p/2745460.html
Copyright © 2011-2022 走看看