《effective c++》读书笔记【一】

      最近决心重新打好编程基础,看effectvie c++以弥补大学三年对基础知识的不重视,希望在阅读的过程中能够复习C++,甚至深入了解C++和编程的一些精髓。按照书的章节顺序,记录下书上较为重要的原话和一些自己的心得。


一、视C++为一个语言联邦

       C++支持很多种语言特性,有着各种能力,可以简单地理解为C++由一个各种次语言组成的联邦而不是一个单一的语言。主要有四种次语言,当切换次语言时,一些守则、策略可能会发生变化。

      四种主要次语言:

     C语言、面向对象c++,泛型C++,STL。

     

    我的理解是:C++是一个由C语言、面向对象、泛型和STL很多种模式组成的集大成者。在不同的模式下用对应的策略进行编程,因地制宜。 同时也可以看出C++的特点:以C语言的面向过程为基础,兼容面向对象、泛型编程的特性,有着强大的STL以供开发者调用。


     二、尽量以inline、enum、const替换#define(宁可以编译器替换预处理器)

     #define DEMO = 1.5 中#define不被视为语言的一部分,所以才是问题的所在。记号名称DEMO也许从未被编译器看见,有可能没进入记号表内。这样导致运用DEMO常量缺获得一个编译错误信息时,提到的是1.5,而不是DEMO。如果DEMO不是自己定义,而是在别人写的头文件中,会让人无法很困惑,并且需要浪费时间去追踪bug。解决的方法是用const代替:const double demo = 1.5; 这样的常量肯定会被编译器看到,而且会比#define导致较小的码。

    以常量const代替#define时有两种特殊情况要注意:

    1.定义常量指针。由于常量定义式通常放在头文件中,因此有必要将指针(而不是指针所指之物)声明为const。若要声明一个常量的char* -base字符串,必须写const两次:

const char* const myName="frank";但是事实上更好的是用string字符串:const std::string myName="frank";

     2.class专属常量,形似于:

<pre name="code" class="cpp">class GamePlayer{
private:
    static const int NumTurns = 5;<pre name="code" class="cpp">    int scores[NumTurns];
    ...
};
 

        但是这样是NumTurns的声明式而非定义式。只要不取地址,可以声明并使用它们而无需提供定义式。但如果要去某个class专属常量的地址,或者一定要看到一个定义式,就必须另外提供定义式如下: 

const int GamePlayer::NumTurns;
       把这个式子放进一个实现文件而非头文件,由于class常量已经在声明时获得了初值,所以可以不再设初值。

      #define无法创建一个class专属常量,因为#defines不重视作用域。一旦宏被定义,它在其后的编译过程中有效。这意味着不能用来提供class专属常量,也不能提供任何封装性。

     旧式的编译器不允许static成员在其声明式上获得初值,此外所谓的“in-class初值设定"也只允许对整数常量进行。所以哟啊将初值放在定义式:

<pre name="code" class="cpp">class CostEstimate{
private:
    static const double FudgeFactor;
    ...
};
const double
      CostEstimate::FudgeFactor = 1.35;

      但是如果在class编译期间需要一个class常量值,例如在上述的GamePlayer::scores的数组声明式中。这时编译器不允许"static整数型class常量”,可改用所谓的“the enum hack”。其理论基础是:“一个属于枚举类型的数值可充ints被使用",于是GamePlayer可定义如下: 

<pre name="code" class="cpp">class GamePlayer{
private:
    enum{NumTurns = 5};<pre name="code" class="cpp">    int scores[NumTurns];
    ...
};
 

     这样用enum来代替整数设定初值的方法某方面很像#define而不像const,例如取一个const的地址是合法的,但是取enum和#define的地址通常不合法。如果不想别人获得一个pointer或reference指向你的某个整数常量,enum可以实现这个约束。另外enum和#define一样绝不会导致非必要的内存分配。 

    另外一个#define误用情况是以它实现宏(macros)。宏看起来像函数,但不会导致函数调用带来的额外开销。例如下面这个宏:

<pre name="code" class="cpp">//以a和b的较大值调用f<pre name="code" class="cpp">#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))
 

     无论何时写出这种宏,都要为所有的实参加上小括号,否则在表达式中调用这个宏可能会遭遇麻烦。而且也会发生一些如下的不可思议事情: 

<pre name="code" class="cpp">int a = 5, b = 0;<pre name="code" class="cpp">CALL_WITH_MAX(++a,b); //a被累加两次<pre name="code" class="cpp">CALL_WITH_MAX(++a,b+10); //a被累加一次
 
 

     调用f之前,a的递增次数取决于”它被用来和谁比较“。 

    事实上,只要写出template inline函数就可以同时获得宏带来的效率以及一般函数的所有可预料行为和类型安全性。

<pre name="code" class="cpp">template<typename T><pre name="code" class="cpp">inline void callWithMax(const T& a, const T& b)<pre name="code" class="cpp">{<pre name="code" class="cpp"> f(a>b?a:b);<pre name="code" class="cpp">}
 
 
 
 

     这样就不需要在函数本体中为每个实参加上小括号,也不担心参数被求值多次。这样的template inline函数是个真正的函数,遵守作用域和访问规则,可以写出一个class内的private inline函数,但是宏就无法完成此事。    

    尽管如此,#define仍然是必需的,而#ifdef/#ifndef也继续扮演控制编译的重要角色。目前预处理器还是有着作用。

    总结:

    1. 对于单纯常量,最好以const对象或enums替换#defines。

    2.对于类似函数的宏,最好改用template inline函数代替#defines。

    

    我的理解:#define无论作为常量和类似函数的宏,日益满足不了程序员开发和面向对象中封装性等的需求,或者说其自身缺陷也越来越被放大。而同时,C++中的const和inline这些特性都可以在功能上替换掉#defines,从而消除#define带来的缺陷。因此才说尽量以inline、enum、const代替#define。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值