Effective C++之条款3,条款4笔记整理
来源:互联网 发布:java jaxbelement 编辑:程序博客网 时间:2024/05/16 15:29
条款03: 尽可能使用const
本条款总结了const的使用场景和使用它带来的好处。
关键字const多才多艺。
char greeting[]="hello":char *p=greeting;//non-const pointer,non-const dataconst char* p=greeting;//non-const pointer,const datachar* const p=greeting;//const pointer,non-const dataconst char* const p=greeting;//const pointer,const data
const语法虽然变化多端,但并不莫测高深,如果关键字const出现在星号左边,表示被指物是常量,如果出现在星号右边,表示指针自身是常量;如果出现在星号两边,表示被指物和指针两者都是常量。
如果被指物是常量,有些程序员会将关键字const写在类型之前,有些人会把它写在类型之后、星号之前。这两种写法的意义相同,所以下面两个函数接收的参数类型是一样的:
void f1(const Widget* pw);void f2(Widget const *pw);
下面是STL中vector迭代器中const迭代器和const_iterator的区别
const std::vector<int>::iterator iter=v.begin();*iter=10;//正确++iter;//错误
std::vector<int>::const_iterator citer=v.begin();*citer=10;//错误++citer;//正确
const最具威力的用法是面对函数声明时的应用。在一个函数声明式内,const可以和函数返回值、各参数、函数自身(如果是成员函数)产生关联。
令函数返回一个常量值,往往可以降低因客户错误而造成的意外,而又不至于放弃安全性和高效性。
例如:
class Rational{......};const Rational operator*(const Rational& lhs,const Rational& rhs);Rational a,b,c;(a*b)=c;if(a*b=c)...
const可以预防“没意义的赋值操作”和“==意外换成=”的烦恼;
const成员函数
将const实施于成员函数的目的,是为了确认该成员函数可以用于const对象身上。这一类成员函数之所以重要,基于两个理由。
1.它们使得class接口比较容易理解,得知哪个函数可以改动对象内容而哪个函数不行。
2.它们使“操作const对象”成为可能
class TextBlock{ public: ... const char& operator[](std::size_t position) const{ return text[position];} char& operator[](std::size_t position){ return text[position];} private: std::string text;}//使用operator[]TextBlock tb("hello");std::cout<<tb[0];//调用non-const[]const TextBlock ctb("world");std::cout<<ctb[0];//调用const[]
std::cout<<tb[0];//正确tb[0]='x';//正确std::cout<<ctb[0];//正确ctb[0]='x';//错误
成员函数如果是const意味着什么?这两个流行概念:bitwise constness和logical constness
bitwise const 阵营的人相信,成员函数只有在不更正对象之任何成员变量时才可以说是const,也就是说它不更改对象内的任何一个bit。
如果成员变量的是一个指针,但是后面改变了指针地址对应的内容,这就说不过去了。
下面使用mutable释放掉non-static成员变量的bitwise constness约束
class CtextBlock{ public: ... std::size_t length() const; private: char* pText; mutable std::size_t textLength;//这些成员变量总是会被更改,即使在const成员函数内 mutable bool lengthIsValid;};std::size_t CTextBlock::length() const{ if(!lengthIsValid) { textLength=std::strlen(pText); lengthIsValid=true; } return textLength;}
此外,一个编程技巧是:当const 和non-const 成员函数有着实质等价的实现时,令non-const 版本调用const 版本可避免代码重复
class TextBlock{ public: ...... const char& operator[](std::size_t position) const{ ... ... ... return text[position];}char& operator[](std::size_t position){ return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);}...};
这里有两次转型,第一次用来*this添加const.第二次则是从const operator[]的返回值移除const
请记住:
某些东西声明为const可帮助编译器侦测出错误用法,const可被施加于任何作用域内的对象,函数参数,函数返回类型、成员函数本体
编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”
当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。
条款04: 确定对象被使用前已先被初始化
本条款告诫程序员,在C++程序设计中,应该对所有对象初始化,以避免不必要的错误,同时,给出了高效初始化对象的方法和正确初始化对象的方法。
(1)初始化构造函数最好使用成员初值列(member initialization list) ,而不要在构造函数本体内使用赋值操作(assignment) 。初值列出的成员变量,其排列次序应该和它们在class 中的声明次序相同。
考虑一个用来表现通讯簿的class ,其构造函数如下:
ABEntry::ABEntry(const string&name,const string& address,const list<PhoneNumber>&phones){ theName=name;//这些都是赋值 theAddress=address;//并非初始化 thePhones=phones; numTimeConsulted=0;}
ABEntry::ABEntry(const string& name,const string& address,const list<PhoneNumber>&phones):theName(name,theAddress(address),thePhones(phones),numTimesConsulted(0))//现在使用的是初始化{//构造函数本体不必有任何动作}
有些情况下即使面对的成员变量属于内置类型(那么其初始化与赋值的成本相同),也一定得使用初始化列表。如果成员变量是const或references,它们就一定需要初值,不能赋值。避免任何麻烦,任何情况下都使用初始化列表
C++ 有着十分固定的”成员初始化次序”。次序总是相同: base class早于其derived classes 被初始化,而class 的成员变量总是以其声明次序被初始化。回头看看ABEntry. 其theName 成员永远最先被初始化,然后是theAddress,再来是thePhones,最后是numTimesConsulted。即使它们在成员初值列中以不同的次序出现(很不幸那是合法的),也不会有任何影响。
(2)C++ 对”定义于不同编译单元内的non-local static 对象”的初始化次序并无明确定义。为免除”跨编译单元之初始化次序”问题,请以local static 对象替换non-local static 对象。
所谓static对象,包括global对象,定义于namespace作用域内的对象、在class内,在函数内、以及在file作用域内被声明为static的对象。函数内的static对象称为local static对象(因为它们对函数而言是local),其他的static对象称为non-local static对象。程序结束时static对象会被自动销毁,也就是它们的析构函数会在main结束时被调用。
class FileSystem{...};FileSystem& tfs(){ static FileSystem fs;//以local static的方式定义和初始化object return fs;}class Directory{...};Directory::Directory(params){ ... std::size_t disks=tfs().numDisks(); ...}Director& tmpDir(){ static Directory td; return td;}
为了避免在对象初始化之前过早的使用它们,你需要做三件事:
1.手工初始化内置型non-member对象
2.使用构造函数成员初始化列表初始化
3.在“初始化次序不确定性”氛围下加强你的设计
请记住:
1.为内置对象进行手工初始化,因为C++不保证初始化它们
2.构造函数最好使用初始化列表,而不要在构造函数中使用赋值操作,其排列次序应该和它们在class中的声明次序相同
3.为避免“跨编译单元之初始化序”问题,请以local static对象代替non-local static 对象
- Effective C++之条款3,条款4笔记整理
- Effective C++之条款1,条款2笔记整理
- 《Effective STL》条款3-条款4
- 《Effective C++》让自己习惯C++:条款1-条款4
- Effective C++:条款01
- Effective C++:条款02
- Effective C++:条款03
- Effective C++:条款04
- Effective C++:条款05
- Effective C++:条款06
- Effective C++:条款07
- Effective C++:条款08
- 《effective C++》条款三
- 《effective C++》条款5
- 《effective C++》条款六
- Effective C++--经验条款
- Effective C ++ 条款34
- 《Effective C++》条款05
- 单片机编程-定时器中断
- Android自定义控件
- 模型类的getDbFields方法改进 查询时排除某字段
- 青涩的思绪 泛起粉色的涟漪
- 我是幸福的
- Effective C++之条款3,条款4笔记整理
- 黑马程序员java笔记之三-----JavaBean
- 单片机编程-排序
- 给盒子的贺文
- 黑马程序员——用AWT写的假记事本,实现退出,保存,打开等功能
- Windows 系统debug级 进程调试工具 ntsd 详解
- LaTeX排版常用字体和格式设置
- 排序算法复习笔记
- IPrincipal 接口相关信息