读书笔记_Effective_C++_条款三:尽可能使用const
2013-03-01 21:43
274 查看
const是常量的意思,它可以定义一个不可改变的量,主要用于以下几个地方:
此时var的值就不能改变了。也正是因为const的变量不能轻易修改存储的值,所以在声明的时候就要初始化,这样就是不行的:
编译器就会报错。
还有一种形式是intconst*p,这种形式是表示常量指针,还是指向常量的指针呢?EffectiveC++给出的建议是看“*”的位置,当const位于星号左侧时,const修饰的是值,即表示指向常量,而当const位于星号右侧时,const修饰的是指针,即表示常量指针。所以intconst*p等价于constint*p,你想对了吗?(关键看const修饰的是*p还是p)。
const有时还会修饰函数的形参或者函数的返回值,都是属于1或2这两种情况。修饰函数形参的用法:
[code]{
[/code]
还有一个地方要注意一下,若有:
[code]{
[/code]
当实参为const时,比如constchar*msg=“hello”,此时fun1(msg)是可以的,但fun2(msg)会报编译错,说是无法将constchar*转成char*;而当实参为普通变量时,比如char*msg=“hello”,fun1(msg)和fun2(msg)都是OK的。这是为什么呢?因为当const的变量传递给非const的变量会不安全(非const的变量可以修改原来定义为常量的东西了!),所以C++限制了这种用法(需用强制类型转换来告诉编译器,编译器才会放行);而反过来,当非const的变量传递给const变量时,不存在安全问题,所以C++编译器总是会放行的。因此,如果在函数体内确实不改变形参a的值,那么采用带const的fun1的写法会更好,适用性更强。
注意这里的fun1()函数后面有一个const,表示这个函数不会修改类的成员变量(在它的函数体里面出现任何改变a或b的值的操作,均不能通过编译);另一方面fun2()函数后面没有const,表示这个函数可能修改类的成员变量,注意这里用的词是“可能”,fun2()可以修改也可以不修改,但为了增强安全性,所以良好的编程风格一般会把不改动成员变量的成员函数修饰为const的。
有一点要切记:有无const是可以构成成员函数的重载的!
在本书中还提到了一个尖锐的问题,如果假定类是这样的:
我们看到,类的成员函数是指针,假定它在构造函数时会被初始化,而指向一段内存空间。那么如果不改变p本身(即指向不变),但是改变了p指向的内容(比如*p=3),这样到底算不算对成员变量进行改动了呢?
读者可以在VS环境中写一下测试用例,可以发现VS编译器对这种情况是放行的,*p=3完全可以通过,但是p=&b就不可以了。
虽然编译器是放过你了,但这也许并不是你的本意,本书中推荐的是“从逻辑上看”,就要交由写代码的你去好好思量一下。如果在某个函数里确实改动了p所指向的内容,那么最好就不要加上const;反过来,如果加上了const就不要改变成员变量,包括它所指向的值。
[code]{
[/code]
可以看到两个重载函数里面的操作都是一样的,别因此认为可以用ctrl+c,ctrl+v而省事了,如果你要改动其中一个函数体里的内容,另一个就要同步更新,而万一你忘记了更新,后果是非常严重的!
一个好的方法来实现同步——在非const的函数中调用const函数!这样来修改:
[code]{
[/code]
说白了,就进行两次转换,一次是把非const的对象(就是自己(*this)转成const对象),但注意返回值要求是非const的,所以用const_cast再进行一次转换就OK了。关于C++转换可以参照本博客的
/article/5198800.html。
1.修饰变量,使之不可改变
举个例子:constintvar=3;
此时var的值就不能改变了。也正是因为const的变量不能轻易修改存储的值,所以在声明的时候就要初始化,这样就是不行的:
constintvar;
编译器就会报错。
2.修饰指针
指针是特殊的变量,有时我们希望对它所指向的对象操作,而有时我们又希望对指针本身进行操作。同样,const应用于指针也有两个含义:一个是指向常量(指向的内容不可更改),一个是常量指针(指针的指向不可更改)。看下面这两个例子:constint*p=&a;/*p为指向常量的指针,即p指向的对象是常量,不可以通过*p=3来修改a的值,但这时p=&b换个指向还是可以的*/ int*constp=&a;/*p为常量指针,即p的指向不可更改,不可以通过p=&b来修改p的指向,但这时*p=3改变a的值还是可以的*/ constint*constp=&a;/*p为指向常量的常量指针,p的指向以及指向的对象都不可以更改,无论是*p=3,还是p=&b都是错误的*/
还有一种形式是intconst*p,这种形式是表示常量指针,还是指向常量的指针呢?EffectiveC++给出的建议是看“*”的位置,当const位于星号左侧时,const修饰的是值,即表示指向常量,而当const位于星号右侧时,const修饰的是指针,即表示常量指针。所以intconst*p等价于constint*p,你想对了吗?(关键看const修饰的是*p还是p)。
const有时还会修饰函数的形参或者函数的返回值,都是属于1或2这两种情况。修饰函数形参的用法:
voidfun(constchara)
[code]{
a=‘d’;//错误,因为的值不可以改变
cout<<a;//OK
}
[/code]
还有一个地方要注意一下,若有:
voidfun1(constchar*a)
[code]{
cout<<a<<endl;
}
voidfun2(char*a)
{
cout<<a<<endl;
}
[/code]
当实参为const时,比如constchar*msg=“hello”,此时fun1(msg)是可以的,但fun2(msg)会报编译错,说是无法将constchar*转成char*;而当实参为普通变量时,比如char*msg=“hello”,fun1(msg)和fun2(msg)都是OK的。这是为什么呢?因为当const的变量传递给非const的变量会不安全(非const的变量可以修改原来定义为常量的东西了!),所以C++限制了这种用法(需用强制类型转换来告诉编译器,编译器才会放行);而反过来,当非const的变量传递给const变量时,不存在安全问题,所以C++编译器总是会放行的。因此,如果在函数体内确实不改变形参a的值,那么采用带const的fun1的写法会更好,适用性更强。
3.修饰迭代器
C++的STL中使用迭代器作为接口,它定义了普通的迭代器,如vector<T>::iterator,也定义了指向常量的迭代器,如vector<T>::const_iterator,初学者可能想当然地认为constvector<T>::iterator等价于vector<T>::const_iterator,其实不是这样的,constvector<T>::iterator表示这个迭代器的指向不可以更改,即表示的是常量迭代器。4.在类中修饰成员函数
const放在类中成员函数的后面,表示这个成为函数不会修改类的成员变量,比如:classA
{
private:
inta;
doubleb;
public:
voidfun1()const;
voidfun2();
};
注意这里的fun1()函数后面有一个const,表示这个函数不会修改类的成员变量(在它的函数体里面出现任何改变a或b的值的操作,均不能通过编译);另一方面fun2()函数后面没有const,表示这个函数可能修改类的成员变量,注意这里用的词是“可能”,fun2()可以修改也可以不修改,但为了增强安全性,所以良好的编程风格一般会把不改动成员变量的成员函数修饰为const的。
有一点要切记:有无const是可以构成成员函数的重载的!
在本书中还提到了一个尖锐的问题,如果假定类是这样的:
classB
{
private:
int*p;
public:
…
};
我们看到,类的成员函数是指针,假定它在构造函数时会被初始化,而指向一段内存空间。那么如果不改变p本身(即指向不变),但是改变了p指向的内容(比如*p=3),这样到底算不算对成员变量进行改动了呢?
读者可以在VS环境中写一下测试用例,可以发现VS编译器对这种情况是放行的,*p=3完全可以通过,但是p=&b就不可以了。
虽然编译器是放过你了,但这也许并不是你的本意,本书中推荐的是“从逻辑上看”,就要交由写代码的你去好好思量一下。如果在某个函数里确实改动了p所指向的内容,那么最好就不要加上const;反过来,如果加上了const就不要改变成员变量,包括它所指向的值。
在const和非const成员函数中避免重复
我觉得这是一个非常重要的内容,有没有加const是构成函数重载的,但通常这种重载的相似度很高,就用书上的例子:classTestBlock
[code]{
private:
stringtext;
public:
...
constchar&operator[](size_tposition)const
{
…
returntext[position];
}
char&operator[](size_tposition)
{
…
returntext[position];
}
};
[/code]
可以看到两个重载函数里面的操作都是一样的,别因此认为可以用ctrl+c,ctrl+v而省事了,如果你要改动其中一个函数体里的内容,另一个就要同步更新,而万一你忘记了更新,后果是非常严重的!
一个好的方法来实现同步——在非const的函数中调用const函数!这样来修改:
char&operator[](size_tposition)
[code]{
returnconst_cast<char&>(
static_cast<constTestBlock&>(*this)[postion]
);
}
[/code]
说白了,就进行两次转换,一次是把非const的对象(就是自己(*this)转成const对象),但注意返回值要求是非const的,所以用const_cast再进行一次转换就OK了。关于C++转换可以参照本博客的
相关文章推荐
- <<Effective c++>>读书笔记---条款20:宁以pass-by-reference-to-const替换pass-by-value
- 读书笔记_Effective_C++_条款四十:明智而审慎地使用多重继承
- Effective C++ Item 03-尽可能使用const
- 读书笔记《Effective c++》 条款03 尽可能使用const
- 读书笔记_Effective_C++_条款二:尽量以const, enum, inline替换#define
- 读书笔记_Effective_C++_条款十八:让接口容易被正确使用,不易被误用
- 读书笔记_Effective_C++_条款六:若不想使用编译器自动生成的函数,就该明确拒绝
- 读书笔记_Effective_C++_条款二十六:尽可能延后变量定义式的出现时间
- Effective C++——》条款3:尽可能使用const .
- effective c++条款21: 尽可能使用const
- effective C++笔记之条款20、21:避免public接口出现数据成员、尽可能使用const
- 读书笔记_Effective_C++_条款四十七:请使用trait classes来表示类型信息
- Effective C++ Item 3 尽可能使用const
- Effective C++条款03解读:尽可能使用const
- effective c++条款3(尽可能使用const)
- 读书笔记_Effective_C++_条款二十:宁以pass-by-reference-to-const替换pass-by-value
- Effective Modern C++ 条款15 尽可能使用constexpr
- 读书笔记_Effective_C++_条款十六:成对使用new和delete时要采取相同的形式
- 读书笔记 effective c++ Item 3 在任何可能的时候使用 const
- 读书笔记_Effective_C++_条款四:确定对象被使用前已先被初始化