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

Effective C++笔记(2)—使用const

2016-08-12 22:41 113 查看

1.尽可能使用const

const的用法一直让人头疼,加上指针或引用就更头疼了,以前特意学习过一次【C++const限定符】,可能那个时候还不够深刻,这次借看书再次学习一蛤。

1.1const修饰指针的形式

先看书中的一个例子:

char greeting[]="Hello";
char *p=greeting;//non-const pointer,non-const data
const char* p = greeting;//non-const pointer,const data
char *const p = greeting;//const pointer,non-const data
const char * const p = greeting;//const pointer,const data


上面的注释会有些迷惑,在这之前,我们清楚的知道,const可以从两个层面去限定:指针本身和指针所指向的内容。因为指针本身只是一个栈上变量而已,他存放的所指对象的地址。

然后就是关于const对指针及其所指向对象的修饰了。

Bjarne在他的The C++ Programming Language里面给出过一个助记的方法:

把一个声明从右向左读( * 读成 pointer to )。

char * const cp;
//cp is a const pointer to char
//即常量指针(指针本身const)

const char * p;
//p is a pointer to const char;
//指向常量的指针(指针所指向的对象const)


在EffectiveC++中,则给出这样一个论断:

如果关键字const出现在星号的左边,则被指物为常量;

如果出现在星号右边,表示自身是常量;

如果出现在星号两边,表示被指物和指针都是常量。

也因为这个论断,我们就清楚了其实下面两条语句其实是同一个东西:

const Widget *pw;
Widget const *pw;
//都是指向常量的指针


1.2char指针和char数组的区别

现在就清楚多了,不过还需要清楚一个区别:

char * greeting="Hello";
char greeting[]="Hello";


首先,“Hello”字符串是存放在字符常量区,在内存中是只读的。

char * greeting="Hello";
表示栈中变量
greeting
存放字符常量区“Hello”的地址,但是由于“Hello”是只读的,于是
*greeting='s';
这样的语句虽然没有error,但是运行时是有问题的。

而对于
char greeting[]="Hello";
来说,会拷贝一份“Hello”到栈中,因此
*greeting='s';
去修改其值是没有问题的。参见:StackOverFlow的回答

这也就解释了
char * greeting="Hello";
没有const限定符修饰,我们可以去修改其对象,但这种修改行为是非法的会带来不可预见的错误。

1.3const迭代器

在STL源码剖析中对迭代器的解释很详细,STL迭代器根据指针的特性塑模出来,其作用就像一个T*指针,而将迭代器加上const限定符,就类似声明一个
T * const
一样(即常量指针),而当我们需要一个不可修改对象内容的迭代器时,使用
const_iterator
,如下:

vector<int> vec;
const vector<int>::iterator it=vec.begin();
*it=10;//ok,改变所指对象内容
++it;//error,类似常量指针
/*******************************/
vector<int>::const_iterator cit=vec.begin();
*cit=10;//error,不能改变所指内容
cit++;//ok,改变所指对象。


1.4const成员函数

const成员函数中仔细学习过一波,对书中所说内容就不觉得陌生。

许多人漠视一个事实:两个成员函数如果只有常量性(constness)不同,可以被重载。

这里就不再赘述其原理,记住成员函数的默认this参数即可,在
EffectiveC++
书中也说,用操作符重载的方式举例也太过造作,举一个简单的例子:

class Test
{
private:
int a;
public:
Test(int x) :a(x){}
void print() const{
cout << "const called" << endl;
cout << "a=" << a << endl;
}
void print(){
cout << "non const called" << endl;
cout << "a=" << a << endl;
}
};

int main(int argc, char**argv)
{
Test t(2);
const Test ct(1);
t.print();//non-const called
ct.print();//const called
system("pause");
return 0;
}


1.5mutable关键字

这里书中提到的两个流派:
physical constness
logical constness
与成员函数修改成员变量的“力度”有关,前者认为,当成员函数只有在不更改对象的任何成员变量时才可以说成const,而成员变量中确实有一些变量需要修改怎么办,即使在const成员函数内?此时就需要用到
mutable
关键字。某些成员变量是需要有mutable特性的,比如在muduo源码中,需要在一些const成员函数中使用Mutex加锁:参见muduo 06 互斥锁和条件变量的封装

1.6casting转型

过段时间会专门花一点时间了解一蛤
const_cast
static_cast
dynamic_cast
这几兄弟,这里简单的理解就是:

const_cast去掉const限定;

static_cast类型转换

说到转型跟const的联系,也就是作者认为,如果一个成员函数既有const的版本又有non-const版本,那么两份代码重复冗余了,因为他们干得活都是一样,为了使用const版本的成员函数来写non-const版本的成员函数,用到上述两种转型:

class TextBlock
{
public:
const char & operator[](size_t position ) const
{
//..
//..
//..
return text[position];
}
char & operator[](size_t position )
{
//static_cast为*this加上const
//const_cast为op[]的返回值移除const
return
const_cast<char&>(
static_cast<const TextBlock&>(*this)
[position]
);
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: