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

来源:互联网 发布:淘宝订单贷款能用多久 编辑:程序博客网 时间:2024/06/02 18:14

1.尽可能使用const

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

1.1const修饰指针的形式

先看书中的一个例子:

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对指针及其所指向对象的修饰了。
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 constnesslogical constness与成员函数修改成员变量的“力度”有关,前者认为,当成员函数只有在不更改对象的任何成员变量时才可以说成const,而成员变量中确实有一些变量需要修改怎么办,即使在const成员函数内?此时就需要用到mutable关键字。某些成员变量是需要有mutable特性的,比如在muduo源码中,需要在一些const成员函数中使用Mutex加锁:参见muduo 06 互斥锁和条件变量的封装

1.6casting转型

过段时间会专门花一点时间了解一蛤const_caststatic_castdynamic_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]            );    }};
1 0