一文带你理顺C++中的const

c++中的const是一个很强大的助手,它允许你指定一个语义约束,表明某一个值不应该被更改,编译器会帮你强制执行这个约束。

const修饰指针

const可以修饰变量,数组,指针,函数,对象等等,几乎可以修饰任何东西,其中比较晦涩的应该是指针了,看下面这段代码

char arr[] = "hello world"; 
char* p = arr;
const char* p = arr; //const修饰arr
char* const p = arr; // const修饰p
const char* const p = arr;//既修饰arr又修饰p

const在*号左边修饰的是指针所指向的值,在*号右边修饰的是指针本身,当然你可以二者结合使用

请注意:以const在*号的左右来辨别其修饰哪一部分,因为有些程序员喜欢将const放在类型之后,*号之前来修饰指针所指向的值,如下两种写法本质是一样的

const char* p;
char const* p;

谈到指针,就不得不谈STL中的迭代器了,使用迭代器就像使用指针一样,但在const层面上,它和指针还是有一些细微差别的

当我们声明一个iterator是const时,其实是表明迭代器本身是const,约束它不能指向别的东西,相当于T* const,而如果想要约束其指向的值,那么你应该使用const_iterator

const std::vector<int>::iterator it;//实际上是修饰it

const修饰函数

const在面对函数声明时,能发挥出很大的威力

const可以修饰函数的返回值

const T& GetValue(int pos);//这个函数返回一个数组对应下标的值的引用

当你确定函数的返回值并不需要被更改时,就加上const,这样做能够有效避免一些令人困扰的问题

上述函数如果我们不加const,用户就可能对数组下标进行无理的更改,这种操作应该被避免,当然也可以不反回引用,这样的话用户得到的是一份拷贝,也就无法进行更改(由于临时变量的常量性,也可能直接给用户报错)。

const可以修饰函数的参数

const修饰参数,可以防止我们编写代码时候由于忘记而去更改不该更改的值,所以在必要的时候使用它

const修饰成员函数

const函数能够告诉我们,哪一个函数能够改动对象内容,哪一个不能。更重要的是,const成员函数提供了一种能够操作const 对象的方法。

在c++类和对象中,const对象是只能调用const 成员函数而不饿能调用非const成员函数的,这是因为const对象将其this指针设置成了const属性,而非const成员函数的函数原型是这样的:

class test
{
    ...
public:
    char& operator[](test& this/*非const成员函数接收非const指针*/,\
                      std::size_t pos);
};

非const成员函数,接收的是非const this指针,而const对象调用非const成员函数传入的是const指针,权限放大,当然不行。

因此const对象只能调用const成员函数,而非cosnt对象既能调用const又能调用非const, 但是非const对象优先调用非const函数,如果没有非const函数才会去调用const函数。

更重要的是,仅是常量性不同的两个函数是可以构成重载的,所以可以实现const对象和非const对象调用同一函数却返回不同结果。

class test
{
    std::string _text;
public:
    test(std::string text)
        :_text(text)
    {}
    const char& operator[](std::size_t pos) const
    {
        std::cout << "I am const" << std::endl;
        return _text[pos];
    }
    char& operator[](std::size_t pos)
    {
        std::cout << "I am not const" << std::endl;
        return _text[pos];
    }
};
int main()
{
    const test t1("yoxi");
    test t2("hello");
    t1.operator[](0);
    t2[1];
    return 0;
}

//结果
I am const
I am not const

bitwise constness 和 logical constness

bitwise constness要求:const成员函数必须保证不能更改对象内的任何一个bit,编译器就是以这种观点进行检测的,所以只要const成员函数中有对成员变量的赋值动作就会立即报错。这种观点为编译器提供了便利,但是有些时候,尽管成员函数不那么const,编译器仍然会让其通过,比如说下面这段代码

class test
{
    char* _text;
public:
    test(char* text)
        :_text(text)
    {}
    char& operator[](std::size_t pos) const
    {
        return _text[pos];
    }
};

这段代码,返回的是对象内部一个数组的引用,因此在外部用户可以随意更改对象内部的值,那我们能说这个const函数是完全const的吗?它确确实实可能导致成员变量的更改,只不过是间接的,这明显和bitwise constness不合。

这也引出了logical constness,这一观点认为,const成员函数可以修改它所处理的对象内部的一些bits。但是编译器只认bitwise constness,因此要使用mutable声明变量以表明他们即使在const成员函数中也能被更改。

#include <iostream>
#include <string>
class test
{
    mutable int a = 1;
public:
    void changeValue() const
    {
        std::cout << "修改前的 a:" << a << std::endl;
        a = 2;
        std::cout << "修改后的 a:" << a << std::endl;
    }
};
int main()
{
    test t;
    t.changeValue();
    return 0;
}
//运行结果
修改前的 a:1
修改后的 a:2

避免non-const和const的重复

前面说过,可以利用non-const和const的重载来完成对const和non-const对象的不同返回结果,但是如果两个函数的完成的工作有较多重复,就可以考虑使用non-const调用const来减少代码重复。比如说一个time类,对于const和non-const对象都需要返回且仅返回年,月,日等信息,那么就可以考虑写出以下代码

#include <iostream>
#include <vector>
class Time
{
    int _year = 1;
    int _month = 1;
    int _day = 1;
public:
    void getTime() const
    {
        std::cout << "I am const" << std::endl;
        std::cout << "年: " << _year << std::endl;
        std::cout << "月: " << _month << std::endl;
        std::cout << "日: " << _day << std::endl;
    }
    void getTime()
    {
        //对this指针作类型转换,以便传入
        static_cast<const Time&>(*this).getTime();
    }
};
int main()
{
    const Time t1;
    Time t2;
    t1.getTime();
    t2.getTime();
    return 0;
}
//运行结果
I am const
年: 1
月: 1
日: 1
I am const
年: 1
月: 1
日: 1

通过const_cast将const函数的this指针去const化,也能实现const调用non-const,但是最好不要那样做,因为const本身就是约束不能更改对象内部的值,而非const则是自由自在的,因此用non-const调用const是安全的,而用const调用non-const就无法保证其安全性了,也失去了const本身的意义

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值