Effective C++ (01)

来源:互联网 发布:网络打印机显示错误 编辑:程序博客网 时间:2024/05/22 12:03

条款1:

C++:

         c、面向对象的c++、带模版的c++、STL 

条款2:尽量以const、enum、inline代替#define

宁可以编译器替换预处理器。#define没有进入符号表,造成无法跟踪。

常量指针。

以const代替#define的所谓常量定义功能

以inline代替#define的所谓宏定义功能

class内部的专属常量。为了将常量的作用域限制于class内,必须让其成为一个类的成员,为了确保这个常量只有一份,必须声明为static。

const int GamePlayer::m_nNumTurns;Class GamePlayer{    Static const int m_nNumTurns = 5;    Int scores[m_nNumTurns];}; const int GamePlayer::m_nNumTurns;

C++通常需要一个定义式,然而如果是class专属常量又是static且为整数类型(int、char、bool),则需要特殊处理。只要不取它们的地址,可以声明并使用它们而无需提供定义式。但如果要取class常量的地址,就必须提供一个定义式:

要放入实现文件而非头文件,由于在声明时已经获得初值,因此定义时不可以在设初值。

声明是原形,定义时具体实现。。。

enum hack:

一个枚举类型的数值可以充当int被使用。

Class GamePlayer{    enum{m_nNumTurns = 5};    Intscores[m_nNumTurns];};


取一个enum的地址不合法,取一个#define也不合法。

如果不想让别人获得一个pointer或reference,enum可以实现。

宏定义:副作用。

用template inline代替宏定义。

 

条款3:尽可能使用const

const出现在*的左边,表示被指物是常量;如果出现在*右边,表示指针本身是常量;如果出现在*两边,则两者都是常量。

const int widget = 1;

int const widget = 1; 

STL的迭代器是以指针为基础的,迭代器的作用类似于T*指针。声明iterator为const就像声明指针为const一样(T* const)。表示这个迭代器不能指向其他不同的东西,但它指向的东西的值是可以变的。

如果希望它所指向的值是不能改变的,需要const_iterator(const T*)

std::vector<int> vec;const std::vector<int>::iterator iter= vec.begin();*itr = 10;  //没有问题++itr;        //错误!常量指针不能再指向其他的地方std::vector<int>::const_iterator itr= vec.begin();*itr = 10; // 错误!++itr;        //没有问题
 函数返回值为const:

有理数的operator*声明

class Rational {…};

const Rational operator* (constRational& lhs, const Rational& rhs);

可以避免下述操作:

Rational a,b,c;

(a*b) = c;

在operator*的结果上再调用operator=。

例如if (a * b = c) // 可能是==

如果是内置类型,直接不合法。因此一个设计良好的用户自定义类型,需要与内置类型无缝对接好。

参数为const:

除非要对参数进行修改并返回,否则尽量声明为const

const成员函数

目的:为了确认该成员函数能够作用于const对象上面。

1:使class接口比较容易被理解。得知哪个函数可以改动对象而哪个不行,加了const的函数一目了然。

2:使操作const对象成为可能。这对高效编程很有效。改善c++性能的一个方法是由传值调用改为传引用,而前提是有const成员函数可以用来处理const对象。

         问题:非const成员函数不能处理const对象???

如果成员两个函数的const性质不同,则可以被重载!

Class TextBlock
{    Const char& operator[](std::size_t position) const // const对象的{    Ruturn text[position];}Char& operator[](std::size_t position) // 非const对象的{    Return text[position];}Private:    Std::string text;}; Textblock tb(“Hello”);Cout << tb[0]; // 调用非const对象的Const TextBlock ctb(“World”);Cout << ctb[0]; // 调用const对象的
真实中:const对象大多用于函数调用的传递结果。

例如:

Void Print(const TextBlock& cbt){    Cout << ctb[0];}

只要重载,给予不同版本不同的返回值。

如何在const成员函数内部修改成员数据的值?mutable。 

Class TextBlock{Const char& operator[](std::size_t position) const // const对象的{    Length = XXX;    Ruturn text[position];}Char& operator[](std::size_t position) // 非const对象的{    Return text[position];}Private:Std::string text;Mutable int length;}; 

如果const和非const的代码有大量重复,则可以用非const的代码调用const的代码 

Class TextBlock{Const char& operator[](std::size_t position) const // const对象的{    Ruturn text[position];}Char& operator[](std::size_t position) // 非const对象的{    // 先将非const对象将其转化为const类型,const返回const char&类型,再将其去掉const性质,转化为char&类型、    // 如果不转化为const对象,则会调用自己,无限循环。。。    Return const_cast<char&> (static_cast<const TextBlock&>(*this) [position]);}Private:Std::string text;};

条款4:确定对象在使用前已被初始化

int x;

int arr[10];

数组不保证被初始化,内容是随机的

int arr[10] = {0}; 

当x是成员函数时,不保证被初始化为0.初始化是程序员的事。

初始化与赋值的区别。类内的成员对象的初始化在初始化列表中完成,即在程序员提供的显式构造函数之前,在程序员提供的显式代码里,被初始化后的内置对象将执行operator=(赋值)操作。

对于类内的内置数据类型(int、char),编译器在成员初始化列表内不管它们,由程序员负责。

当想让初始化列表调用对象的默认构造函数时,可以:theObj()。编译器会自动给你调用对象的默认构造函数。但是尽量将所有的成员都在初始化列表中出现!以避免遗漏内置类型

如果是const、reference则必须在初始化列表中初始化!因为const、reference只能被初始化一次,如果没有在初始化列表中提供,则编译器会默认给其初始化,而程序员则不能再显式的更改它们!

初始化次序:父类(按照继承顺序)、成员对象(按照声明顺序)。即使在初始化列表中顺序被打乱,也无影响。

所谓static对象,其寿命从被构造出来直到程序结束为止,因此stack和heap-based对象都被排除。

static的对象:定义于global的、定义于namespace的、在classes内的对象,这些对象没有static关键字。在函数内、在file作用域内声明为static的对象。在函数内声明为static的对象为局部对象,其他的都是non-local static。它们的析构函数在main结束后自动调用。

函数内的static对象称为local static对象(因为它们对函数而言是local)

non-local static:定义于global的、定义于namespace的、在classes内的对象、file作用域内声明的static对象

如果一个非局部的static对象的初始化用到了另一个编译单元的某个非局部的static对象,而这个对象可能尚未被初始化!对于多个目标文件(编译单元)的非局部static的初始化次序没有定义。

class FileSystem{pubic:         size_tnumDisk() const;};extern FileSystem tfs; class directory {pubic:         Directory(params);};Directory::Directory(params){         size_tdisks = tfs.numDisks();}

假设创建一个Directory对象:

Directory tempDir(params);

这里需要保证tfs在tempDir之前被初始化。但是它们定义于不同编译单元的非局部static对象。如何能保证tfs在tempDir之前先被初始化?

无法决定它们的初始化次序

extern : global对象 

将每一个非局部static对象搬到自己的专属函数内(对象被声明为static),函数返回一个指向它的引用,类似于单例模式?

C++保证,局部static对象会在该函数被首次调用期间被初始化 

class FileSystem{pubic:         size_tnumDisk() const;};FileSystem& tfs(){         staticFileSystem fs;         returnfs;} class directory {pubic:         Directory(params);};Directory::Directory(params){         size_tdisks = tfs.numDisks();}Directory& tempDir(){static Directorytd;return td;}

多线程下带有不稳定性

记住:

为内置对象(int char bool)手工初始化

构造函数最好使用初始化列表