Loading

【C++ 基础】const 关键字

0 概览

本文开发环境为 Linux 操作系统(x86) + C++,概览如下:

  • const 的含义
  • const 能修饰什么?分别有什么作用
    • const 修饰变量
    • const 修饰指针
    • const 修饰引用
    • const 修饰函数
    • const 修饰类
  • const 与 define 的区别

1 const 含义

被它修饰的值不能改变,是只读变量。必须在定义的时候就给它赋初值(如果是类成员变量也可使用初始化列表进行初始化)

2 const 与指针

const 与指针分别 3 种

  • 指向常量的指针(pointer to const)
  • 自身是常量的指针(常量指针,const pointer)
  • 前面两种的组合:指向常量的常指针

2.1 指向常量的指针(pointer to const)/ 底层 const

指向常量的指针(pointer to const):定义了一个指针,这个指针指向一个只读的对象,不能通过该指针来改变这个对象的值。

特点:强调的是指针所指向的对象的不可改变性

底层 const:底层 const(low-level const)表示指针所指得对象是一个常量

形式:

const 数据类型 *指针变量 = 变量名;
数据类型 const *指针变量 = 变量名;

示例代码:

int a = 2;
int b = 3;

// 以下两种写法都可
const int *p = &a;
int const *p = &a;

// 更改
*p = 10;    // 错误,不可更改指向对象的值
*p = &b;    // 正确,可以指向别的对象
a = 10;     // 正确,在原对象上修改

2.2 自身是常量的指针(常量指针,const pointer)/ 顶层 const

自身是常量的指针(常量指针,const pointer):定义了一个指针,这个指针的值只能在定义时初始化。

特点:强调的是指针的不可改变性

顶层 const:顶层 const(top-level const)表示指针本身是一个常量

形式:

const 数据类型 *指针变量 = 变量名;
数据类型 const *指针变量 = 变量名;

示例代码:

int a = 2;
int b = 3;

int *const p = &a;

// 更改
*p = 10;     // 正确,可以改变指向对象的值
p = &b;      // 错误,不可指向别的对象
a = 10;      // 正确,在原对象上修改

2.3 指向常量的常指针

指向常量的常指针:是前面两种指针类型的组合(3.1,3.2)

特点:既不可改变指向的地址 且 指向的对象的内容不可透过指针修改

形式:

const 数据类型 *const 指针变量 = 变量名;

示例代码:

int a = 2;
int b = 3;

const int *const p = &a;

// 更改
*p = 10;   // 错误,不可更改指向对象的值
p = &b;    // 错误,不可指向别的对象
a = 10;    // 正确,在原对象上修改

2.4 记忆方式

第一种方式:利用英文从右边往左边读,并且以 to 为分界,to 之前为描述指针的特性,to 之后为描述指针指向的对象的特性

// (1) p is a pointer to const char
// (1) p 是一个指针指向 const char,即指针所指向的 char 变量的值不可透过指针修改
const char *p; 
char const *p; 

//(2)p is a const pointer to char
//(2)p 是一个 const 指针指向一个 char,即 p 不可改变指向的地址
char *const p; 

//(3)p is a const pointer to const char
//(3)p 是一个 const 指针指向 const char,即 p 不可改变指向的地址,也不可改变指向的对象的值
const char *const p; 

第二种方式:

如果 const 位于 * 的侧,则 const 就是用来修饰指针所指向的对象,即指针指向的对象不可透过指针修改
如果 const 位于 * 的侧,则 const 就是修饰指针本身,即指针本身是常量,不可改变指向的地址

2.5 小结

1、当指向的目标特性为 const,则指针指向的对象的内容不可透过指针修改

2、当指针被加上 const 特性,则指针不可改变指向的地址

3、当上述两种都包含的情况,则指针不可改变指向的地址 且 指针指向的对象的内容不可透过指针修改

3 const 与引用

只有一种

  • 对常量的引用(reference to const)
  • 没有 const reference,因为引用只是对象的别名,引用不是对象,不能用 const 修饰

3.1 对常量的引用(reference to const)- 简称常量引用

特点:不能修改他所绑定的对象

形式:

const 数据类型 &引用变量 = 变量名;

示例代码 1 引用及其对应的对象都是常量:

const int a = 2;

const int &reference1 = a;  // 正确:引用及其对应的对象都是常量
int &reference2 = &a;       // 错误:不能让一个非常量引用指向一个常量对象

reference1 = 10;            // 错误:常量引用不不能修改他所绑定的对象

示例代码 2 引用为常量,其对应的对象不是常量:

int a = 2;

const int &reference1 = a;  // 正确:引用是常量,其对象不是常量
int &reference2 = &a;       // 正确:引用和其对象都不是常量

reference1 = 10;            // 错误:常量引用不不能修改他所绑定的对象
reference2 = 10;            // 正确:普通引用可以修改他所绑定的对象

4 const 与函数

4.1 const 修饰函数返回值

示例代码:

const int function5();      // 返回一个常数
const int* function6();     // 返回一个指向常量的指针变量,使用:const int *p = function6();
int* const function7();     // 返回一个指向变量的常指针,使用:int* const p = function7();

4.2 const 修饰函数参数

示例代码:

void function1(const int Var);           // 传递过来的参数在函数内不可变
void function2(const char* Var);         // 参数指针所指内容为常量
void function3(char* const Var);         // 参数指针为常量
void function4(const int& Var);          // 引用参数在函数内为常量

5 const 与类

总结:

  • 常对象成员可以在定义的时候赋值或使用初始化列表进行初始化
  • 常对象成员函数可用于对重载函数的区分
  • 常对象只能调用常成员函数

示例代码 1(常对象成员可以使用初始化列表进行初始化):

class A
{
private:
    const int a;                // 常对象成员,只能使用初始化列表进行初始化

public:
    // 构造函数
    A() : a(0) { };             // 初始化列表
    A(int x) : a(x) { };        // 初始化列表

    // const 可用于对重载函数的区分
    int getValue();             // 普通成员函数
    int getValue() const;       // 常成员函数,不得修改类中的任何数据成员的值
};

void function()
{
    // 对象
    A b;                        // 普通对象,可以调用全部成员函数
    const A a;                  // 常对象,只能调用常成员函数
}

示例代码 2(常对象成员不能被赋值):

#include <stdio.h>
#include <string>

using namespace std;

class MyClass {
public:
    const int myConstMember;

    MyClass();

    void setMember();
};

void MyClass::setMember()
{
    myConstMember = 3;     // 因为常量不可被修改,因此会报错
}

int main(int argc, char* argv[])
{
    MyClass testClass;

    return 0;
}

运行截图,如下所示:

image

示例代码 3(常对象成员可以在定义的时候赋值):

#include <stdio.h>
#include <string>
#include <iostream>

using namespace std;

class MyClass {
public:
    const int myConstMember = 4;
};

int main(int argc, char* argv[])
{
    MyClass testClass;
    cout << "myConstMember:" << testClass.myConstMember << endl;

    return 0;
}

运行截图,如下所示:

image

6 const 与 #define 的区别

宏定义 #define const 常量
宏定义,相当于字符替换 常量声明
预处理器处理 编译器处理
无类型安全检查 有类型安全检查
不分配内存 要分配内存
存储在代码段 局部常量存放在栈区;全局常量编译期一般放在符号表中,不分配内存;字面值常量,比如字符串,存放在常量区

提问:上述表格有提到数据段和代码段,栈区,常量区,他们究竟是什么?对于内存来说还有其他段吗,他们分别存储程序中的什么呢?

可以参考下一篇博客:【八股文 02】C++ 进程内存布局及其相关知识- https://www.cnblogs.com/PikapBai/p/17577466.html

7 总结

  • 修饰变量,说明该变量不可以被改变
  • 修饰指针
    • 指向常量的指针(pointer to const):强调的是指针所指向的对象的不可改变性
    • 自身是常量的指针(常量指针,const pointer):强调的是指针的不可改变性
    • 前面两种的组合:指向常量的常指针:既强调指针所指向的对象的不可改变性又强调指针的不可改变性
  • 修饰引用
    • 指向常量的引用(reference to const):不能修改他所绑定的对象。用于形参类型,即避免了拷贝,又避免了函数对值的修改:
  • 修饰类成员函数,可用于重载函数的区分,也说明该成员函数内不能修改成员变量
  • 修饰类成员变量,只能在赋值时初始化或使用初始化列表进行初始化
  • 修饰类,只能调用调用类常成员函数

8 参考资料

1、《C++ Primer 第五版》- 2.4 const 限定符

2、const那些事 - 作者 Light-City - https://light-city.github.io/basic_content/const/#4

3、interview - 作者 huihut - https://github.com/huihut/interviewhttps://interview.huihut.com/#/?id=const

posted @ 2023-07-19 11:41  她爱喝水  阅读(456)  评论(0编辑  收藏  举报