Effctive C++ 阅读笔记之让自己习惯C++(01~04)

让自己习惯C++

条款01:把C++ 看成一个语言联邦

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

条款03:尽量可能使用const

条款04:确定对象被使用前就引进被初始化

一、把C++ 看成一个语言联邦

释义: 将c++视为一个由相关语言组成的联邦而非单一语言。

解析:

 组成c++语言联邦的次语言:

c语言: 区块,语句,预处理器,内置数据类型,数组,指针等。

object-riented c++: class(构造,析构),封装,继承,多态,virtual(虚函数)等。(面向对象设计之古典原则)

Templatec++: 泛型编程STL:Template库,容器,算法、迭代器。

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

二、尽量以const,enum,inline替换#define

释义: 宁可以编译器替换预处理器

解析:

1、使用#define定义常量
#define ASPECTRATIO 1.653

不足:
  a、因为或许define不被视为语言的一部分,于是ASPECTRATIO有可能不会进入symbo table(记号表)。当运用此常量获得一个编译错误时,编译器可能会提到1.653而不是ASPECT RATIO,导致追踪错误不方便。尤其是当这个常量没有定义在自己的头文件内,追踪错误会花较长时间。

  b、因为预处理器会盲目的替换宏,所以会导致目标码(object code)出现多份儿1.653。

解决: 使用常量替换宏。

const double AspectRatio = 1.653;

优点:

  a、作为一个语言常量肯定会进入symboltabl,所以定位错误方便。
  b、只有一份儿1.653。

特殊情况:

  1、定义常量指针:常量的定义通常在头文件内(以便被不同的源码含入),因此有必要将指针声明为const。如果需要一个定义一个常量字符串const std:;string authorName ="LALA"要比 const char* const authorName="IALA"合适的多。

  2、class专属常量:

const确定常量,static保证唯一,成员变量限制范围。

class GamePlayer
{
private:
    static constint NumTurns=5;  //常量声明式而非定义式
    int scores[NumTurns];        //使用该常量
}

NumTurns为整数类型(integral type),只要不取地址,可以声明并使用。如果需要取地址或者编译器执着的要求定义式,就必须提供定义式。

const int GamePlayer::NumTurns; //定义(需放入实现文件(.cpp),声明时已设初值,
                                //此处不可以再设初值

有些编译器可能不支持上述语法或者面对非整数型变量,就只能在类内只能声明,不能设定初值,必须在定义文件中初始化。

class ConstEstimatelprivate:
{
    static const double FudgeFactor;    //常量声明
}

const double ConstEstimate::FudgeFactor=1.35;    //常量定义

当编译器不允许"static 整数型class常量"完成"in class初值设定"(类内),可改用"the enum hack"补偿做法。

理论基础: 属于枚举类型的数值可权充ints被使用。

class GamePlayer[private
{
    enum {NumTurns=5};

    int scores[NumTurns];

}

enumhack(枚举):

  1、不可以取地址,比较像#define而不是const。

  2、模板元编程的基础技术。

2、#define类似函数的宏
#define CALLWITHMAX(a,b) f((a)>(b)? (a):(b))

缺点:
 a.必须为每一个实参加上小括号。

 b.++a,参数被求值多次。

解决:

templateinline函数

template<typenameT>inline void callwithMax(const T a,const T b)
{
    f(a>b?a:b);
}

优点: 和普通函数一样遵守作用域和访问规则,可以被封装。

总结: 对于单纯常量,最好以const对象或enum替换#define,对于形似函数的宏,最好改用inline函数替换#define。

三、尽量可能使用const

释义: 如果某值不变是事实,那就应该指出来。使用const修饰。
解析: C++中const关键字的作用(转)

总结:

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

  • 编译器强制实施bitwise constness,但你编写程序时应该使用"概念上的常量性"。

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

四、确定对象被使用前就引进被初始化

释义: 关于"将对象初始化"这事,c++似乎反复无常。

解析: 因为c++是一个语言联邦,所以在c++中初始化行为却不是很明确,c语言代码不会进行初始化,但是vector却有保证(STL)。读取未初始化的值会导致不明确行为。

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

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;          //这些都是赋值(assignments), theAddress   =  address;        
	thePhones   =  phones;			//而非初始化(initializations)。
	nurnTimesConsulted  =  O;
}

注意赋值和初始化的区别,c++规定,对象的成员变量的初始化动作发生在进入构造函数之前。

比较合适的写法是:

ABEntity::ABEntity(const std::string& name,const std::string& address,const std::list<PoneNumber>& phone):theName(name),theAddress(address),thePhones(phones),numPimesConsulted(0)
{
}

如果想默认构造成员变量也可以这样,只要指定无物(nothing)作为初始化实参即可。

ABEntity::ABEntity():theName(),theAddress(),thePhones(),numTimessulted(0)
{

}

  内置类型的初始化与赋值的成本相同,如果是内置类型也一定使用初始化列表。因为,如果成员变量是const 或者 eference,就必须定需要初值,不能被赋值。为了简化记忆,都使用初始化列表比较合适。总是使用成员初值列。这样做有时候绝对必要,且又往往比赋值更高效。

  c++有着十分固定的成员初始化次序,class的成员变量总是以其声明的次序进行初始化,及时初始化列表采用不同的顺序,也不会对初始化顺序造成影响。为了避免不必要的麻烦,最好是以声明顺序为初始化顺序。

  static对象包括global对象,定义于namespace作用域内的对象,在class内、函数内、以及在file作用域内声明的static对象。

  函数内static对象是local-static对象,函数外static对象是non-local-static对象。

  编译单元是指产出单一目标文件的那些源码。基本上是单一源文件加上其含入的头文件。

  c++对"定义于不同编译单元内的non-localstatic对象"的初始化次序并无明确定义,可以将每个non-local-static对象放到自己专属函数内变成local-static,并返回一个reference指向它所含的对象这是ingleton模式的一个常见的实现手法。

基础: c++保证,函数内的1ocal gtatic对象会在"该函数被调用期间""首次遇上该对象定义式"时被初始化。

优势: 保证初始化,如果未调用函数,不会引发构造和析构成本。reference-returning函数,内含"static对象",使他们在多线程系统中带有不确定性,解决办法是在程序的单线程启动阶段手工调用所有的函数。

总结:

  • 为内置型类型进行手工初始化,因为c++不保证初始化他们。
  • 构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同。
  • 为避免"跨编译单元之初始化次序"问题请以local static对象替换non-local-static对象。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值