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应当提供一个普通函数(而非在析构函数中)处理。
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应当提供一个普通函数(而非在析构函数中)处理。
相关文章推荐
- Effective C++学习笔记-01
- Effective C++学习笔记 条款07:为多态基类声明virtual析构函数
- 操作系统学习笔记_01_OS概念简述
- Dotnet B/S 架构学习笔记_01(2008-10-05)
- ucos学习笔记01---OSUnMapTbl的理解
- FC网络学习笔记01 分类: 网络基础 2013-10-10 09:42 865人阅读 评论(0) 收藏
- 学习笔记 07-01-04
- mini2440 uboot-2011.12 学习笔记 之01 采用JLink+ADS1.2调试uboot的方法
- python核心编程学习笔记-2016-08-02-01-读取文件的函数中的文件指针问题
- 【Linux学习笔记】Linux_01_基本命令、文件管理、正则
- Effective C++学习笔记(十六)
- 音频引擎IrrKlang学习笔记01:播放控制与音效设置
- 【Spring】Spring学习笔记-01-入门级实例
- 【慕课网】php工程师学习计划之我的学习笔记——01 入门必学web基础 htmlcss基础课程 篇
- 【S2HS学习笔记】第三章节:Hiberbate的基本用法01
- linux 学习笔记01 top
- effective c++学习笔记3
- CMake 学习笔记 01 - CMake 简介
- 机器学习基石--学习笔记01--linear hard SVM
- 【8-23】MFC学习笔记 01