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

本文深入探讨了C++中const关键字的应用,包括const如何修饰指针、const迭代器的使用场景、const成员函数的特点及mutable关键字的作用。通过具体示例说明了const在不同上下文中的意义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 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]
            );
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值