让自己习惯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对象。