Effective c++ 1 1...4
导读
Pass-by-value意味着调用copy构造函数。
尽可能避开undefined behavior。
条款01:视c++为一个语言联邦。
四个次语言,C、OO、template、STL。
每个次语言都有自己的规约。
当从一个次语言切换到另一个次语言时,高效编程的守则要求你改变策略…用哪种策略?取决于你使用C++的哪一个部分。
条款002:尽量以const enum inline 替换#define
#define不提供任何封装性。#define到#undef之间均可见。
#define有可能被编译器忽视,没进入symbol table。
const double AspectRatio = 1.653;代替 #define ASPECT_RATIO 1.653
const char* const autorName = “S M”;
const std::string autorName = “S M”;
enum hack技术提供三个好处:
1、 编译器不支持类内给static const int 赋初值,而你又需要用。Just as a note also, it was most useful when defining array sizes which need to be known when a class is declared Where you would probably want to do this with a static constant, as the compiler did not support it, it would work correctly with an enum declaration.
1 class GamePlayer{ 2 /*static const int NumTurns = 5;*/ 3 enum{NumTurns = 5}; 4 int scores[NumTurns]; 5 }; 6 //const int GamePlayer::NumTurns;
2、 取一个enum的地址不合法,你需要这种约束,不希望某个pointer或者reference获得你的整数常量。
3、 enum绝不会导致非必要的内存分配。
1 //用 2 template< typename T > 3 inline void callWithMax(const T& a, const T& b) 4 { 5 f( a>b ? a : b ); 6 } 7 //代替 8 #define CALL_WITH_MAX(a,b) f( (a)>(b) ? (a) : (b) )
#include #ifdef/#ifndef 仍在使用
条款003:尽可能使用const
1、
const在*左侧,对象是常量 const char* p;
char const* p;
const在*右侧,指针是常量 char * const p;
2、const可以帮助编译器侦测出错误的用法。
1 class Rational{ 2 const Rational operator*( const Rational& lhs, const Rational& rhs) }; 3 Rational a,b; 4 //a*b 的结果是const 就避免了a*b == c 错写成 a*b=c时的赋值。
3、类可以产生const对象(不变的对象),这种const对象只能调用const成员函数,const成员函数保证(不变的对象)这点。
类有成员函数和const成员函数,这两种成员函数的区别关键是 this指针。const成员函数的this指针是 const指针。自然也只能返回const的this指针/const的this对象引用(如果没有强转的话)。不改变const对象。
当我的const对象的某些位确实需要修改的,const成员函数需要修改const对象的个别位个别数据成员时,用mutable释放掉这个数据成员的const,绕过编译器的bitwise constness要求。
1 class cTextBlock{ 2 public: 3 size_t length() const; 4 private: 5 char* pText; 6 /*mutable*/size_t textLength; 7 /*mutable*/bool lengthIsVaild; 8 }; 9 10 size_t cTextBlock::length() const 11 { 12 if (!lengthIsVaild) 13 { 14 textLength = std::strlen(pText); 15 lengthIsVaild = true; 16 } 17 return textLength; 18 } 19 20 /*我还是需要length()是const函数,在取length的时候不打算改变pText指向的文字内容,const对象可以保证不改变文字内容的情况下获得文字长度。这是逻辑上的const需求。 21 22 但是textLength和lengthIsVaild成员在求length的时候要改变,length()const成员函数需要改变const对象的个别数据成员textLength和lengthIsVaild,那么就把这两个数据成员设成mutbale!*/
4、在const和non-const成员函数的实现中避免重复
令non-const成员函数调用const的重载版本来实现自己,避免代码重复。
调用重载版本不能直接用名字调,会递归自身。而是把this强转为 const this ,然后用const this执行的调用就是调的const版本的了。 调用完const版本后的返回类型可能是const的,视需要强转为non-const版本的。至此,完成non-const版本函数的实现。
const_cast<[const] T >( …);
条款004: 对象被使用前已经初始化
1、 内置类型对象 2、类数据成员对象 3、static对象
1、 内置类型对象。
定义的时候就初始化它。作为类数据成员的时候,在初始化列表里一块儿了。
2、 类数据成员对象。
对象成员变量的初始化动作发生在进入构造函数本体之前。推出1:初始化列表里完成。推出2:初始化时的try-catch应该在初始化列表处。
节省成本:
初始化列表的过程:调用一次copy构造。
构造函数本体中赋值的过程:先调一次default构造,再调一次copy assignment。
适用不能赋值的成员:const和reference类型成员,这种成员必须被初始化。
如果构造函数N多,又不想一一写初始化列表:
可以适当遗漏 “赋值表现和初始化一样好”的成员变量,把这些成员变量的赋值写到一个private函数里,然后让N个构造函数调用这个函数。适用于“初值是由文件或者数据库读入”的情况。不过,这终归是“伪初始化”。
C++有十分固定的成员初始化次序:先初始化基类的,然后初始化派生类的。类中按照声明次序初始化。 推出:初始化列表的次序不决定初始化次序,还是写得和声明次序一样比较好。
初始化列表中可能是这种形式:先基类的(像调用基类构造函数似的),然后是自己类的成员变量的(按照声明次序)。
3、 static对象。
生命:从被构造出来到程序结束为止。
有哪些种:
local static:在函数内部定义的static对象。local static对象在“该函数被调用期间”,“首次遇上该对象之定义式”时被初始化。
non-local static:不在函数内的(global的、namespace内的、class内的、file作用域内的)
编译单元:产生单一目标文件的那些源码:若干.h文件+源码文件。最后形成一个single object file。估计就一个main。???
问题:不同编译单元中定义的non-local static对象的初始化次序不确定
1 //编译单元1file1 2 class Filesystem{ 3 size_t numDisk() const; 4 }; 5 extern Filesystem tfs; 6 7 //编译单元2file2 8 class Directory{ 9 public: 10 Directory(params); 11 }; 12 13 Directory::Directory(params){ 14 size_t disks = tfs.numDisk(); 15 } 16 17 int main() { 18 19 Directory tempDir(params); 20 21 } 22 /*构造tempDir对象的时候,需要用到tfs对象。需要保证tfs对象构造在tempDir对象构造之前。这个,C++是不保证的。我们如何保证?*/
把static放到函数里,变作local static,这个函数返回static的引用。第一次调用这个函数时,local static被构造,然后返回它的引用,就获得了一个保证被构造的static对象。还可以根据调用函数的次序保证构造次序。
1 //编译单元1file1 2 class Filesystem{ 3 size_t numDisk() const; 4 }; 5 inline Filesystem& tfs(){//reference-returning 函数 6 static Filesystem fs; 7 return fs; 8 } 9 10 //编译单元2file2 11 class Directory{ 12 public: 13 Directory(params); 14 }; 15 16 Directory::Directory(params){ 17 size_t disks = tfs().numDisk(); 18 } 19 Inline Directory& tempDir(){//reference-returning 函数 20 static Directory td; 21 return td; 22 } 23 24 int main() { 25 tempDir(); 26 }
多线程中使用 non-const static有问题。
解决1:在程序的单线程启动阶段手工调用全部的reference-returning函数,可以消除与初始化有关的竞速形势(race condition)。
解决2:双检测锁。