《Effective C++》(一)

来源:互联网 发布:java实现文件加密 编辑:程序博客网 时间:2024/04/26 04:46

  • 让自己习惯C
    • 条款01视C为一个语言联邦
    • 条款02尽量以constenuminline替换define
    • 条款03尽可能使用const
    • 条款04确定对象被使用前已先被初始化

1 让自己习惯C++

条款01:视C++为一个语言联邦

1.C++是个多重范型编程语言:procedural (过程形式)、object-oriented (面向对象形式)、functional (函数形式)、generic (泛型形式)、metaprogramming (元编程形式 )
2.C++的4个次语言:C、Object-Oriented C++、Template C++、STL
3.小结:C++高效编程守则会视状况而变化,取决于使用C++的哪一个部分

条款02:尽量以const、enum、inline替换#define

1.#define 所设置的内容会在预处理器的时候被移走(预处理时全部替换),因此编译器会看不到它,有可能对我们追踪、查错造成很多不必要的时间上的浪费
2.浮点常量而言,使用常量const可能比#define导致较小量的码
3.定义常量指针。由于常量定义式通常被放在头文件内,因此有必要将指针声明为const;
4.类的专属常量。为了将常量的作用域限制于class内,必须让其成为该class的一个成员,而为确保此常量至多只有一份实体,必须让其成为一个static成员。这个用#define做不到,因为#define 并不重视 作用域,一旦被定义,它在其后的编译过程都有效,除非在某处#undef。注意如下代码:

class GamePlayer{private:    static const int NumTurns = 5; //常量声明,此处是声明而非定义。    int scores[NumTurns]; //使用该常量    ...};//class内的常量static而且integral type(整数类型),只要不取其地址,可以声明并使用而无需提供定义式//如果要取其地址,或编译器要求提供定义式则如下定义(在.cpp文件中):const int GamePlayer::NumTurns;// 常量已在声明时获得初值,就无须再设初值

5.“the enum hack”:一个属于枚举类型的数值可权充int被使用

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

6.另一个常见#define误用情况是用它来实现macros(宏)

// 以a和b的较大值调用f  #define CALL_WITH_MAX(a,b) f( (a) > (b) ? (a) : (b) ) a = 5 , b = 0 ;  CALL_WITH_MAX(++a,b);    // a被累加两次  CALL_WITH_MAX(++a,b+10);    // a被累加一次/*宏替换后如下:f((++a)>(b)?(++a):(b));f((++a)>(b+10)?(++a):(b+10));?:运算符就是先判断?前面的表达式是否为真,为真的话就运行:前面的代码,否则就运行:后面的代码*/

这里,调用f之前,a的累加次数,竟然取决于“它被拿来和谁比较”!这时,你就需要一个 template inline 函数 解决问题:

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

7.小结:
<1>对于单纯常量,最好以const 对象或者enum 替换 #define;
<2>对于形似函数的宏,最好改用 inline函数 替换 #define

条款03:尽可能使用const

1.如果const出现在星号(*)左边,表示被指针所指的物是常量;如果出现在星号右边,表示指针自身是常量;如果出现在星号两边,表示被指物和指针都是常量
2.令函数返回一个常量值,往往可以降低因客户错误而造成的意外,而又不至于放弃安全性和高效性。避免(a*b)=c的错误
3.对于函数参数,除非你有需要改动参数或local const对象,否则请将它们声明为const。传递指向常量的引用
4.将const实施于成员函数的目的:它们使classes接口比较容易理解(可以明确知道哪个函数可以改动对象内容,而哪个不能);它们使“操作const对象”成为可能。 这个是 “pass by reference to const 方式传递对象”技术的基础。
5.小结:
<1> 将某些东西声明为 const 可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。
<2> 编译器强制实施bitwise constness,但你编写程序时应该使用 conceptual constness(概念上的常量性)。
<3> 当const和non-const 成员函数有着实质等价的实现时,令non-const版本用const版本可避免代码重复。

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

1.如果使用C part of C++ 而且初始化可能招致运行期成本,那么就不保证发生初始化,但若进入non-C parts of C++ 规则就会有些变化。例如来自C part of C++ 的array 不保证它的内容被初始化,而来自STL part of C++的 vector 却有这个保证。
2.对于无任何成员的内置类型,必须手工完成初始化;而对于非内置类型,初始化的责任就在 constructors(构造函数)身上:确保每一个构造函数都将对象的每一个成员初始化。
3.注意别混淆赋值和初始化:

class PhoneNumber  {...};  class ABEntry  {  public:      ABEntry( const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones );  private:      std::string theName;      std::string theAddress;      std::list<PhoneNumber> thePhones;      int numTimesConsulted;  };  ABEntry::ABEntry( const std::string& name , const std::string& address, const std::list<PhoneNumber>& phones)  {      theName = name;// 这些都是赋值,并非初始化      theAddress = address;      thePhones = phones;      numTimesConsulted = 0;  }  ABEntry::ABEntry( const std::string& name , const std::string& address, const std::list<PhoneNumber>& phones)  :theName(name),// 这些都是初始化  theAddress(address),  thePhone(phones),  numTimesConsulted(0)  {// 构造函数本体不必有任何动作  } 

4.总是使用成员初值列,列出所有成员变量
5.许多classes 拥有多个构造函数,每个构造函数有自己的成员初值列,多份成员初始列就会导致重复动作。

解决:这种情况就可以合理的在初值列中遗漏那些“赋值操作和初始化一样好”的成员变量,把它们用赋值操作,并将这些操作移到一个private函数,供所有构造函数使用,这种做法尤其适用在:“成员变量的初值是由文件或数据库读入”的时候。当然相对于这种的“伪初始化“,还是用成员初值列的“真正初始化“通常更可取。

6.C++ 有着十分固定的“成员初始化次序”,次序就是 base classes 更早于其 derived classes 被初始化。classes成员变量总是以其声明的顺序次序初始化。
7.C++ 对“定义于不同编译单元内的non-local static 对象”的初始化相对次序并无明确定义。

解决:将每一个non-local static对象搬到自己的专属函数内(该对象在此函数声明为static),这些函数返回一个reference指向它所含的对象,这样 non-local static 对象被 local static对象替换了。而C++保证,函数内的local static 对象会在“该函数被调用期间”“首次遇上该对象定义式”时被初始化。而且这样做以后,如果在执行期间,没有调用 non-local static 的仿真函数,就绝对不会引发构造和析构成本,这也是原来的non-local static没有的好处。

8.小结:
<1>为内置型对象进行手工初始化,因为C++不保证初始化它们。
<2>构造函数最好用 成员初值列 ,而不要在构造函数中使用赋值操作。初值列列出的成员变量,应该按照其在类内的声明次序进行排序。
<3>为免除”跨编译单元之初始化次序“问题,请以local static对象替换 non-local static对象。

0 0
原创粉丝点击