Effective c++ 第一章总结

来源:互联网 发布:unity3d圣典app 编辑:程序博客网 时间:2024/05/22 10:54
1.视C++为一个语言联邦。
由:C,Object-Oriented C++,Template C++,STL组成
每个部分的函数传值都不一样:
C(内置数据类型):pass-by-value
Object-Oriented C++(对象类型):pass-by-reference-to-const
Template C++(泛型):pass-by-reference-to-const
STL(指针类型):pass-by-value




2.尽量以const,enum,inline替换#define
原因:
a.#define 不方便调试,当发现#define的代码有问题,不能定位到具体是哪里的问题。
const变量的定义:
const double aspectRatio=1.653;
const char* const authorName="Scott Meyers";
const std::string authorName("Scott Meyers");一般用这个替换上面的常量字符串定义


b.#define 不能创建class的专属常量,因为#define并不重视作用域,不能提供任何封装性。
用const定义class的专属常量:
class GamePlayer
{
private:
static const int NumTurns=5;//常量声明式
int scores[NumTurns];//使用该常量
...
};
注意:通常C++要求你对你所使用的任何东西提供一个定义式
但是只要满足3个条件(1.属于class的专属常量 2.是static类型 3.是整数类(integral type:ints,chars,bools))就无需定义式。
但如果你要取class专属常量的地址,或纵使你不取其地址而你的编译器坚持要定义式,那就必须提供定义式。
上面类的NumTurns参数的定义式如下:
const int GamePlayer::NumTurns;//定义式,先前已经赋值,这里不能再赋值了。
不过编译器不支持在声明式赋初值,则可以将初值移动到定义式里面。这是上面的类中就不能定义数组了,可以定义如下:
class GamePlayer
{
private:
enum{ NumTurns = 5 };//常量声明式
int scores[NumTurns];//使用该常量
...
};


如果你不想让别人获得一个pointer或者reference指向你的某个整数常量,enum可以帮助你实现这个约束。


c.有些宏定义的像函数,这样可能会导致不可思议的结果。
例:
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))
int a=5,b=0;
CALL_WITH_MAX(++a,b);//a 被累加2次
改为:
template<typename T> //由于我们不知道
inline void callWithMax(const T& a,const T& b) //T是什么,所以采用
{ //pass by reference to const
f(a > b ? a : b);
}


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




3.尽可能使用const.
std::vector<int> vec;
...
const std::vector<int>::iterator iter=vec.begin();//iter的作用像个T* const
*iter=10;//没问题
++iter;//错误


std::vector<int>::const_iterator iter=vec.begin();//iter的作用像个const T*;
*iter=10;//错误
++iter;//没问题


const 最具威力的用法是面对函数声明时的应用。在一个函数声明式内,const可以和函数返回值,各参数,函数自身(如果是成员函数)参数关联。


a.令函数返回一个常量值,往往可以降低因客户错误而造成的意外,而又不至于放弃安全性和高效性。
例:
class Rational{..};
const Rational operator* (const Rational& lhs,const Rational& rhs);
如果不这样定义,下面的表达式是成立的:
Rational a,b,c;
(a*b)=c;//因为a*b返回的是Rational类型,所以可以被赋值,如果加了const就变成常量了,是不能被赋值的。
b.将const实施于成员函数的目的,是为了确认该成员函数可作用于const对象身上。
第一,知道哪个函数可以改动对象,哪个函数不行,很是重要。
第二,它们使“操作const对象”成为可能。
许多人漠视一件事实,两个成员函数如果只是常量性不同,可以被重载。
class TextBlock
{
public:
...
const char& operator[](std::size_t position) const //operator[] for
{ return text[position]; }  //const对象


char& operator[](std::size_t position)  //operator[] for
{ return text[position]; }  //non-const对象


//以上函数还有一个实现方式
char& operator[](std::size_t position)  //operator[] for
{ return   //non-const对象
const_cast<char&>(static_cast<const TextBlock&>(*this)[position]); } 
private:
std::string text;
};


TextBlock tb("Hello");
std::cout<<tb[0];//调用non-const函数


const TextBlock tb("Hello");
std::cout<<tb[0];//调用const函数


如果函数的返回类型是个内置类型,那么改动函数的返回值从来就不合法。
如果想在一个const成员函数中修改成员变量,则可将变量定义为 mutable 类型。


const成员函数承诺绝对不改变其对象的逻辑状态。


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




4.确定对象被使用前已先被初始化
请立下一个规则,规定总是在初值列中列出所有的成员变量,并且赋初值。
如果成员变量是const或references,它们就一定需要在初值列中初始化,不能被赋值。


许多class拥有多个构造函数,每个构造函数有自己的成员初值列。如果这钟class存在许多成员变量或base class,多份成员初值列的存在就会导致
不受欢迎的重复。这种情况下可以合理地在初值列中遗漏哪些“赋值表现像初始化一样好”的成员变量。改用他们的赋值操作,并将哪些赋值操作
移往某个函数(通常是private),供所有构造函数调用。这种做法在“成员变量的初值系由文件或数据库读入”时特别有用。


C++ 有着十分固定的“成员初始化次序”。是的,次序总是相同:base classes更早于其derived classes被初始化,class的成员变量总是以其声明
次序被初始化。


“不同编译单元内定义之non-local static对象 ”的初始化次序:
static对象:其寿命从被构造出来直到程序结束为止,析构函数会在main()函数结束时被自动调用。stack和heap-based对象都被排除。这种对象包括global对象,namespace作用域对象,class内对象,函数内对象,file作用域内对象。
local static对象:函数内的static对象,因为他们对函数而言是local。
non-local static对象:local static对象外的其他对象。


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


C++ 对“定义于不同的编译编译单元内的non-local static对象”的初始化相对次序并无明确定义。针对这个问题可以用单例模式来解决。
原因:C++保证,函数内的local static对象会在“该函数被调用期间”“首次遇上改对象之定义式”时被初始化。
class CFileSystem{};
CFileSystem& tfs()
{
static CFileSystem fs;
return fs;
}

不同编译单元内定义的non-local static 对象的初始化顺序



总结:为避免在对象初始化之前过早地使用它们,你需要做3件事。
第一,手工初始化内置型(non-member)对象。
第二,使用成员初值列对付对象的所有成分。
第三,在初始化次序不确定性时加强你的设计。(不同编译单元所定义的non-local static对象的初始化顺序不确定,要用单例模式解决)



0 0