• 《Effective C++ 》学习笔记——条款02




    ****************************  一、 Accustoming Yourself to C++ **************************** 


    条款02: Prefer consts,enums,and inlines to #defines


    上一个条款,让我正确认识C++,并不是是一个一体语言,而是一个联邦,

    而这一个条款。是在纠正我们的一些行为习惯。


    尽可能的用const、enum、inline 而非 #define

    这个就要扯到 编译器预处理器 的方面了,

    #define 所设置的内容会在预处理器的时候被移走,因此编译器会看不到它。

    这时候。会出现这样的问题:

    #define ASPECT_RATIO 1.653

    而当 ASPECT_RATIO 出问题时。不会报错这个常量。而是报错  1.653 这个数字,

    ——Why? 

    所使用的名称可能并未进入到symbol table 内。

    在编译器中,会将这些变量常量名称  归纳到 symbol table(记号表)内。

    可是,这个常量被预处理器移走了。因此。没有进入 symbol table,所以假设编译出错,错误信息会提到1.653 而非ASPECT_RATIO

    这就对我们追踪,查错造成非常多不必要的时间上的浪费。

    假设用const取代:

    const double AspectRatio = 1.653;  // 由于非#define 名称也要有对应规范的变化

    这时,这个常量名就会进入 symbol table。因此假设编译出错,错误信息回是 AspectRatio


    不只如此。对于浮点常量而言,使用常量const可能比#define导致较小量的码,由于预处理器“盲目”将宏名称 ASPECT_RATIO 替换成 1.653 可能导致 object code ( 目标码 )出现多份 1.653 ,若使用const 则会杜绝同样情况。


    还有,对于const 替换 #define 还有两种特殊情况

    ① 定义 constant pointers (常量指针)

    在头文件定义一个常量指针须要用两个const,我的理解是,一个定义指针不能指向别的地址。一个是定义指针内容不可变。

    这个详细还会在下一个条款详细讨论。 

    再有一点就是对于定义一个字符串。

    用 const std::string authorName("Scott Meyers");

    优于 const char* const authorName = "Scott Meyers";


    ② 关于class的专属常量

    这是有关于 scope(域) 的一个概念,假设想将一个常量作用域限制于一个类内。你就必须让这个常量成为类内的一个 member(成员),这时为了让这个类内常量至多仅仅有一个实体,还必须让它成一个 static(静态) 成员。

    当然,这个用#define做不到,由于#define 并不重视 作用域,一旦被定义,它在其后的编译过程都有效,除非在某处#undef。所以 const 是可被封装的。

    另一点,在类内的const是声明式而非定义式,假设编译器非要求你出示一个定义式给它,能够这样实现:

    class GamePlayer {

    private:

    static const int NumTurns = 5;

    int scores[NumTurns];

    ......

    };


    const int GamePlayer::NumTurns; // 常量已在声明时获得初值。就无须再设初值

    这就是 "in-class" 初值设定,

    这个设定也有限制,要求class内的常量static并且integral type(整数类型){比如:ints,chars,bools}

    但此处限制是对于整数类型的,假设是其它类型。能够类内声明不设初值。直接在类外定义时再设初值




    OK,关于const 说完,接下来就是 enum


    对于上述情况,假设类在编译期间须要一个class常量值,而恰巧编译器不支持或不同意 static 整数型 class 常量 完毕 in-class 初值设定,那就能够使用 the enum hack 补偿方法

    它的理论基础是—— 一个属于 enumerated type(枚举类型)的数值可权充int被使用

    例:

    class GamePlayer{
    private:
        enum { NumTurns = 5 };
        int scores[NumTurns];
        ......
    };

    当然,enum作用不只这样,有非常多理由让我们。必须认识它

    enum hack 行为比較像 #define 而非 const 。取const地址是合法的。而取enum和#define地址一般是不合法的,enum 和 #define 一样绝不会导致非必要内存的浪费

    ② enum hack  是 template metaprogramming (模板元编程)的基础技术




    then 是 inline 方面


    有一个常见的#define误用情况是用它来实现macros(宏)

    宏看起来像函数,但不会招致 function call (函数调用)带来额外开销。

    以下这个样例,就是宏夹着宏实參,调用函数f:

    // 以a和b的较大值调用f
    #define CALL_WITH_MAX(a,b) f( (a) > (b) ?

    (a) : (b) )


    说实话。宏长这样真的会让人痛苦啊。。。

    这还仅仅是简单的取较大的调用f,假设复杂点的函数,那画面不敢想!

    PS:要注意。不管什么时候写带实參的宏,要为宏中全部的实參带上小括号。

    调用它的样例:

    int a = 5 , b = 0 ;
    CALL_WITH_MAX(++a,b);    // a被累加两次
    CALL_WITH_MAX(++a,b+10);    // a被累加一次

    这里,调用f之前,a的累加次数。居然取决于“它被拿来和谁比較”!


    这时,你就须要一个 template inline 函数 解决这个问题:

    template<typename T>
    inline void callWithMax( const T& a, const T& b){
        f( a > b ?

    a : b ); }


    此外,因为这是一个真正的函数,它遵守scope(作用域)的概念和訪问规则,因此。全然能够封装在类内,当然这是宏无法做到的。


    结束语:

    尽管 有了const、enum、inline以后,我们对预处理器,尤其是 #define 的需求减少了,但并不是全然消除。#include 仍然是必需品,#ifdef/#ifndef也继续扮演控制编译的重要角色。眼下还没有到预处理器隐退的时候,可是我们能够明白的给它更长的假期。



    Please remember:

    ① 对于单纯常量。最好以const 对象或者enum 替换 #define

    ② 对于形似函数的宏,最好改用 inline函数 替换 #define



    End....

    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    《0908-构建之法:现代软件工程-阅读笔记》
    配置fabric-crashlytics教程
    iOS开发工具——统计Crash的工具Crashlytics
    Overview of iOS Crash Reporting Tools: Part 2/2
    Overview of iOS Crash Reporting Tools: Part 1/2
    添加自签发的 SSL 证书为受信任的根证书
    UIWebView to view self signed websites (No private api, not NSURLConnection)
    Has anybody found a way to load HTTPS pages with an invalid server certificate using UIWebView?
    AFNetworking 2.0 Tutorial
    USING CHARLES FROM AN IPHONE
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4946153.html
Copyright © 2020-2023  润新知