Effective C++读书笔记(一)

一、让自己习惯C++


   条款01:视C++为一个语言联邦
   
C++视为一个由相关语言组成的联邦而非单一语言,由四个主要的次语言组成。

  • C。说到底C++仍是以C为基础。区块,语句,预处理器,内置数据类型,数组,指针统统来自C。
  • Object-Oreinted C++。这一部分也就是C with Classes所诉求的。类,封装,继承,多态,virtual函数(动态绑定)。
  • Template C++。这是C++泛型编程部分。由于templates威力强大,它们带来崭新的编程范型,也就是所谓的template metaprogramming。
  • STL。STL是个template程序库。它对容器,迭代器,算法以及函数对象的规约有极佳的紧密配合与协调。

 请记住:

  • C++高效编程守则视状况而变化,取决于你使用C++的哪一部分。

条款02:尽量以constenuminline替换#define

  • const替换#define

#define  ASPECT_RATIO 1.653

这样你的ASPECT_RATIO可能并未进入记号表,导致你对1.653以及它来自何处毫无概念,于是你将因为追踪它而浪费时间。

解决之道:const double  AspectRatio = 1.653;

AspectRatio肯定会被编译器看到,当然就会进入记号表内。

常量替换#define两点注意:

定义常量指针:

const char *authorName= “Shenzi”;
cosnt std::string authorName("Shenzi");

建议使用第二种。

类专属常量:

为了将常量的作用域限制于class内,你必须让它成为class的一个成员;而为确保常量至多只有一份实体,你必须让它成为一个static成员:

class GamePlayer

{

     private

          static const int NumTurns = 5//常量声明式,如果不取它们的地址,你可以声明并使用它们而无须提供定义式。

           int scores[NumTurns]//使用该常量

          ….

}

然而#define不能够用来定义class专属常量。

  • enum替换#define

万一你编译器不允许“static整数型class常量”完成“in class初值设定”,可改用所谓的“the enum hack”补偿做法。

class GamePlayer

{

      private

          enum{NumTurns=5};

          intscores[NumTurns]

          ….

}

取一个const的地址是合法的,但取一个enum的地址就不合法,而取一个#define的地址通常也不合法。如果你不想让别人获取一个pointerreference指向你的某个整数常量,enum可以帮助你实现这个约束。

  • inline替换#define

宏有着太多的缺点,光是想到它们就让人痛苦不堪。

#define CALL_WITH_MAX(a,b)    f((a) > (b)) ? (a) :(b))

替换:
template<typename T>

inline voidcallWithMax(cosnt T &a, cosnt T &b)

{
            f(a > b ? a : b);
}

 请记住:

  • 对于单纯常量,最好以const对象或enums替换#defines
  • 对于形似函数的宏,最好改用inline函数替换#defines        

条款03:尽可能使用const条款

关键字const多才多艺:

面对指针:

char greeting[] = "Hello";
char *p = greeting;//non-const pointer,non-const data;
const char *p = greeting; //non-const pointer,const data;

char * const p = greeting; //constpointer,non-const data;
const char * const p = greeting;// const pointer, const data;

面对STL

const std::vector<int>::interator iter =vec.begin();//作用像T *const,++iter错误! iterconst
 std::vector<int>::const_iterator cIter = vec.begin();//
作用像const T**cIter = 10错误! *cIterconst

令函数返回一个常量值,往往可以降低因客户错误而造成的意外,而不至于放弃安全性和高效性。

 例:const Rational operator*const Rational &lhs, cosnt Rational &rhs//防止出现  c= a*b

  • const成员函数

两个成员函数如果只是常量性不同,可以被重载。

成员函数如果是const,这有两个流行概念:bitwise constnesslogical constnessbitwise const阵营的人相信,成员函数只有在不更改对象之任何成员变量时才可以说是const。也就是说它不更改对象内的任何一个bit。它对常量性的定义,因此const成员函数不可以更改对象内任何non-static成员变量。

不幸的是许多成员函数虽然不十足具备const性质却能通过bitwise测试。更具体地说,一个更改了“指针所指物”的成员函数虽然不能算是const,但如果只有指针(而非其所指物)隶属于对象,那么此函数为bitwise const不会引发编译器异议。

class CTextBlock

{

     public:

           char &operator[](std::size_t position) const //bitwise const声明,

           {returnpText[position];}                              //但其实不适当

};

这个class不适当地将其operator[]声明为const成员函数,而该函数却返回一个reference指向对象内部值。

constCTextBlock cctb(“Hello”);

char *pc =&cctb[0];

*pt =’J’;//但你终究还是改变了它的值。

利用mutable释放掉non-static成员变量的bitwise constness约束。

  • constnon-const成员函数中避免重复

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

请记住:

  • 将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体;
  • 编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的车辆”(conceptual constness);
  • 当cosnt和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。

条款04:确定对象被使用前已先被初始化

永远在使用对象之前先将它初始化。对于无任何成员的内置类型,你必须手工完成此事。至于内置类型以外的任何其它东西,初始化责任落在构造函数身上,确保每一个构造函数都将对象的每一个成员初始化。

  • 赋值和初始化:

 C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。所以应将成员变量的初始化置于构造函数的初始化列表中。
     ABEntry::ABEntry(conststd::string& name, const std::string& address,conststd::list<PhoneNumber>& phones)
 { 
            theName = name;//这些都是赋值,而非初始化

            theAddress = address;//这些成员变量在进入函数体之前已调用默认构造函数,接着又调用赋值函数。
            thePhones = phones;
            numTimesConsulted = 0;
    } 

ABEntry::ABEntry(const std::string& name, conststd::string& address,conststd::list<PhoneNumber>& phones) 
        : theName(name),  //
这些才是初始化 
        theAddress(address), //
这些成员变量只用相应的值进行拷贝构造函数,所以通常效率更高。
        thePhones(phones),
        numTimesConsulted(0){    } 

如果成员变量时const或reference,它们就一定需要初值,不能被赋值。 C++有着十分固定的“成员初始化次序”。次序总是相同:base classes更早于其derivedclasses被初始化,而class的成员变量总是以其声明次序被初始化。当在成员初始化列表中列各成员时,最好总是以其声明次序为次序。

  • non-local static对象初始化次序

如果某编译单元内的某个non-local static对象的初始化动作使用了另一编译单元内的某个non-local static对象,它所用到的这个对象可能尚未初始化,因为c++对“定义于不同编译单元内的non-local static对象”的初始化次序并无明确定义。

将每个non-local static对象搬到自己的专属函数内(该对象在次函数内被声明为static)。这些函数返回一个reference指向它所含的对象。所以non-local static对象被local static对象替换了。这是Singleton模式的一个常见实现手法。

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

请记住:

  • 为内置对象进行手工初始化,因为C++不保证初始化它们;
  • 构造函数最好使用成员初始化列表,而不要在构造函数本体内使用赋值操作。初始化列表列出的成员变量,其排列次序应该和它们在类中的声明次序相同;
  • 为免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象。   


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值