您的位置:首页 > 编程语言 > C语言/C++

Effective C++学习笔记-01

2012-12-29 22:16 316 查看
1.条款03:尽量使用const

1.1 const可以修饰指针,指针所指物,两者或者都不是const对象。注意const的位置,位于*号左边的是修饰所指物为常量, *右边是修饰指针

const char* p = greeting //const data, non-const pointer

char* const p = greeting //const pointer, non-const data

const char* const p = greeting // all the const

例子,以迭代器中的T*指针来说明具体的用法:

std::vector<int> vec;

...

const std::vector<int>::iterator iter = vec.begin(); // iter作用是T* const

*iter = 10; //可以改变指针所指

iter++; //报错,不能修改iter常量

std::vector<int>::const_iterator cIter = vec.begin(); //cIter作用是const T*

*cIter = 10; //不可以修改指针指向

cIter++; //没问题,可以改变所指物

下列两个函数的参数所返回的参数类型是一致的

void f1(const Widget* pw) // f1和f2都返回了一个指针指向常量Widget

void f2(Widget const* pw)

1.2 const成员函数: 通过声明const成员函数有两个好处,第一:使得class接口更容易被理 解,什么内容是不能被修改的 第二:提升C++效率,pass-by-reference-to-const. PS:两个成员函数如果是常量,一样可以被重载。

1.2.1 bitwise constness and logical constness

2.条款04:确定对象被使用前已经被初始化Make sure that objects are initialized before they're used.

2.1对于内置类型之外的对象的初始化,主要由构造函数来实现,所以确保每个构造函数把所有的成员变量初始化。

2.2【理解】注意不要混淆了初始化(initialization)和赋值(assignment),参考下面的例子:

赋值操作:

class PhoneNumber{...};

class ABEntry{

private:

std::string theName;

std::string theAddress;

std::List<PhoneNumber> thePhones;

int numTimesConsulted;

public:

ABEntry(const std::string& name, const std::string& address, const std::List<PhoneNumber>& phones);

};

//构造函数进行赋值(assignments)而并非是初始化(initializations)

ABEntry::ABEntry(const std::string& name, const std::string& address, const std::List<PhoneNumber>& phones){

theName = name;

theAddress = address

thePhones = phones;

numTimesConsulted = 0;

}

//构造函数初始化的最佳写法是使用MemberInitialization List(成员初始化列表)的方式实现

ABEntry::ABEntry(const std::string& name, const std::string& address, const std::List<PhoneNumber>& phones)

: theName(name), theAddress(address), thePhones(phones), numTimesConsulted(0)

{}

这个构造函数比上面的效率更高!上面那个基于赋值的那个构造函数要首先调用一个default默认构造函数设置成员变量初始值。

2.3 为免除“跨编译单元之初始化次序”问题,以local static对象替换non-local static对象

第二:构造函数,析构函数,赋值

1.条款05 了解C++默默调用了哪些函数 Know what functions C++ silently writes and calls

1.1 空类Empty class,C++处理之后会为它添加默认的构造函数,一个赋值操作符和一个析构函数。

如果你写了 class Empty{}; 经过C++处理后相当于:

class Empty{

public:

Empty(){...}; //default构造函数

Empty(const Empty& rhs){..}; //copy构造函数

~Empty(){...}; //析构函数,是否为Virtual?

Empty& operator=(const Empty& rhs){} //copy assignment操作符

}

【请记住】:编译器暗自为class创建default构造函数,copy构造函数,析构函数和copy assignments操作符。

2.条款06 若不想使用编译器自动生成的函数,就该明确拒绝。

为了不使用编译器自动提供创建函数的功能,可将相应的成员函数声明为private并且不予实现。使用像Uncopyable这样的的base class也是一个方法。

//Uncopyable实现阻止编译器自动创建函数

class Uncopyable{

protected:

Uncopyable(){};

~Uncopyable(){};

private:

Uncopyable(const Uncopyable&); //阻止copy构造函数

Uncopyable& operator=(const Unconpyable&);

}

为了防止HomeSale对象被拷贝,通过继承Uncopyable类便可以避免

class Homesale: private Uncopyable{

... //class将不再自动声明 copy构造函数以及copy assignment操作符。

...

}

3. 条款07 为多态基类声明virtual析构函数(Declare destructors virtual in polymorphic base classes)

【请记住】:

3.1 Polymorphic(带多态性质的)的基类base class,应该声明一个virtual析构函数。如果一个class存在任何一个virtual函数,避免因为子类继承过程中带来的问题。应该拥有声明一个virtual析构函数。

3.2 Classes设计的目的如果不是为了实现多态作为基类来使用,就不该声明virtual析构函数。

错误使用方法

class SpecialString: public std::string{ //馊主意!std::string中有个non-virtual函数

....

};

SpecialString* pw = new SpecialString("Impending Doom");

std::string* ps;

...

ps = pw; //SpecialString* = string*

delete ps; //未定义!*ps的SpecialString资源会泄露

//因为SpeicalString的析构函数没被调用

4. 条款08 别让异常逃离析构函数(Prevents exceptions from leaving deconstructors)

【请记住】

4.1 析构函数绝对不要吐出异常,如果一个被析构函数调用的函数可能抛出异常,那么析构函数应当捕捉任何异常,然后吞下他们或者结束程序。

4.2 如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应当提供一个普通函数(而非在析构函数中)处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++