c++笔记:我所理解的const

c++笔记:我所理解的const


编写程序的时候,我们希望定义个变量,它的值不能被改变,为了满足这一需求,可以用const关键词进行限定。const关键字可用于c++各种环境:

  1. 变量

  2. 引用和指针

  3. 函数参数和返回值

  4. 类成员变量

  5. 类成员函数

const修饰变量

当const修饰于变量时,其值不能改变。即在声明变量之后,不允许进行赋值操作


void const_variables()

{

  const int a = 20;

  // a++;    // error:对只读变量进行赋值操作

}

const 修饰引用

与 const 修饰变量相似,引用本身和引用对象是一体的,所以只存在一种情况。const 引用本身不可变,const 引用对象根据对象属性决定是否可变。


void const_reference()

{

    int a = 20;

    const int & r = a;

    r++;                // error : 引用本身不可变

    const int b = 20;

    const int & rb = b;

    b++;                // error: 引用对象不可变

    rb++;              // error: 引用本身不可变

}

const 修饰指针

const 对于指针修饰操作,可分为三种方式:修饰指针指向、修饰指针本身或同时修饰指针指向和本身


char hello[] = "hello";

修饰指针指向


const char *p = hello;  // 指针可变,指针指向不可变

修饰指针本身


char * const p = hello; // 指针不可变,指针指向可变

同时修饰指针本身和指向


const char * const p = hello;  // 指针不可变,指针指向不可变

《Effective c++》 中介绍了记忆方法:

如果关键字const出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针自身是常量;如果出现在星号两边,表示被指物和指针两者都是常量。


void const_point()

{

    char a[] = "hello";

    char b[] = "world";

    const char * p1 = a;

    p1[2] = 'm';        // error: 指针本身不可变

    p1 = b;            // 指针指向可变

    char * const p2 = a;

    p2[2] = 'm';        // 指针本身可变

    p2 = b;            // error: 指针指向不可变

    const char * const p3 = a;

    p3[2] = 'm';        // error: 指针本身不可变

    p3 = b;            // error: 指针指向不可变

}

const函数参数和返回类型

对于 const 修饰函数参数,与 const 修饰指针或者变量类似。如果函数内部对函数参数不进行修改操作,则可以对参数用const 修饰。变量、指针或者引用都可以进行操作。

当 const 修饰类对象引用时,对于性能还有一定提升。常规类对象作为参数,要经历 构造、拷贝、析构等流程,如果用const引用,不仅直接保护该对象本身,同时也省去了构造等一些流程。


// @para: _a  _a值不可变

// @para: _p2  _p1指针本身不可变,_p1指向对象内容可变

// @para: _p2  _p2指针本身和内容都不可变

// @para: _data _data不可变

const char _str[] = "const";

void const_func_args(const int _a, const char * _p1, const char * const _p2, const Data & _data) {

    _a++; // error

    _p1 = _str;

    _p1[2] = 'm';  //error

    _p2 = _str;    // error

    _p2[2] = 'm';  // error

}

对于 const 返回值,记住其中一点即可。const函数返回值不可当左值使用,一定程度上可以防止用户误写代码导致的错误。《Effective c++》中就有一个样例,重载 * 运算符,误使用导致不必要的麻烦。


class Rational {...};

const Rational operator *(const Rational &lhs, const Rational &rhs);

Rational a, b, c;

(a * b) = c; // 误写代码导致不必要的麻烦.

const成员变量

c++ const成员变量和普通const变量用法相似。不过也存在明显的区别:

  • const 成员变量只能通过声明和初始化列表初始化

  • 相同类不同对象的const成员变量值可能不一样。

const 成员变量初始化

由于const变量的特性,const变量在声明之后就不可改变。对于c++类来说,成员变量生命之后可在构造函数内部进行初始化,这样违背了const变量的特性。不过构造函数还支持初始化列表初始化,对于 const成员变量可在成员初始化列表中进行初始化


class ConstMember

{

public:

    ConstMember(int _mem) : mem_(_mem) { // 可以在初始化列表中初始化

        mem_ = 20;      // error: 不能在构造函数中对const成员变量进行初始化

    }

private:

    const int mem_ = 30;        // 根据c++类特性,可在类内声明时初始化

};

相同类不同对象的const成员变量值可能不一样

当const成员变量在初始化列表中初始化时,可能调用的是有参构造函数,这样在声明类对象的时候,const成员变量值会根据类对象的参数改变


ConstMember member1(30); // const成员变量值为30

ConstMember member2(40); // const成员变量值为40

const成员函数

const成员函数目的也是保护数据,写法是在函数声明尾部加上const


class ConstFunc

{

public:

    void GetVal() const;

private:

    int val_;

};

void ConstFunc::GetVal() const

{

    return this->val_;

}

注意:const成员函数声明和定义尾部都需要加上const

上述demo中,显示写出了this指针。这个是c++类成员函数的特性,除static成员函数的所有成员函数都会隐式绑定this指针。默认情况下,this的类型是指向类型非常量版本的常量指针,即 ConstFunc * const。尽管this指针为隐式的,this指针也遵循初始化规则,意味着我们不能把this绑定到一个常量对象上。这种情况也导致了我们不能在常量对象上调用普通成员函数。

  • 不能改变成员变量的值

  • 不能调用普通成员函数


class ConstFunc

{

public:

    int GetVal() const {

        SetVal(20); // error: 不能在常量对象上调用普通成员函数

        data_++; // error: 不能在常量对象上修改成员变量

        return val_;

    }

    void SetVal(int _val) { val_ = _val; }

private:

    int val_;

    int data_;

};

const成员函数重载


class ConstOverload

{

public:

    void GetValue();

    void GetValue() const; // 可视为函数重载

};

其他

const 常量表达式 替代预处理宏 #define

当文件中需要定义常量的时候,常规使用 #define 宏定义,可用 const 对 #define 进行替换,原因如下:

  • define 属于预处理器的语法,在代码层面只进行简单的文本替换。不进入 symbol table 中,对于 debug 和排错有一定的难度。

  • const 是 c++ 编译器语法,会提供类型检查和调试信息。


// #define MAX_LEN  200

// 使用 const 替代 #define

const int MAX_LEN = 200;

小知识

对于 const 修饰的变量,有些同学会将 const 关键字写在类型之前,有些会写在类型之后、星号之前。这两种写法的意义是一样的。


const int * a;

int const * a;

参考文档

《effective c++》:

  • 条款03:尽量以 const、enum、inline 替换#define(Prefer const,enums,and inlines to #defines)

  • 条款03:尽可能使用const(Use const whenever possbile)

《c++ primer》第五版:

  • 第二章 2.4 const限定符

  • 第六章 6.2 const形参和实参

  • 第七章 7.1 设计Sales_data类

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容