当前位置: 首页 > news >正文

C++---const关键字 编译期约束保证数据的 只读性

const 是 C++ 中最基础也最核心的关键字之一,其设计初衷是通过编译期约束保证数据的“只读性”,从而提高代码的安全性、可读性和可维护性。它的用法贯穿变量、指针、引用、类、函数等几乎所有语法场景。

一、const 修饰变量:定义“只读”常量

const 最基本的用法是修饰变量,使其成为“常量”——即初始化后不可修改。这是编译期的约束,编译器会对修改行为直接报错。

const int a = 10;  // 常量 a,初始化后不可修改
a = 20;  // 编译错误:assignment of read-only variable 'a'
关键特性:
  1. 必须初始化const 变量定义时必须初始化(否则后续无法赋值),初始化值可以是常量表达式或运行时计算结果:

    const int b;  // 编译错误:uninitialized const 'b'
    const int c = 3 + 4;  // 合法:常量表达式初始化
    int d = 5;
    const int e = d;  // 合法:运行时变量初始化(e 仍为只读)
    
  2. 作用域与链接性

    • 局部 const 变量:作用域为当前块,无链接性。
    • 全局 const 变量:默认内部链接(仅当前文件可见),与非 const 全局变量的外部链接不同。若需跨文件访问,需显式加 extern
      // file1.cpp
      extern const int g_val = 100;  // 外部链接,可被其他文件访问// file2.cpp
      extern const int g_val;  // 声明,使用 file1 中的 g_val
      
二、const 与指针:三种组合形式

指针是 const 应用中最容易混淆的场景,核心是区分“指针指向的内容不可改”和“指针本身不可改”,具体有三种组合:

形式含义能否修改指针指向的内容能否修改指针本身(指向新地址)
const int* p指向常量的指针(底层 const)不能
int* const p常量指针(顶层 const)不能
const int* const p指向常量的常量指针(底层+顶层 const)不能不能

示例解析

int x = 10, y = 20;// 1. 指向常量的指针(const 修饰 int)
const int* p1 = &x;
*p1 = 30;  // 编译错误:不能修改指向的内容
p1 = &y;   // 合法:可以指向新地址// 2. 常量指针(const 修饰指针 p2)
int* const p2 = &x;
*p2 = 30;  // 合法:可以修改指向的内容
p2 = &y;   // 编译错误:不能修改指针本身// 3. 指向常量的常量指针
const int* const p3 = &x;
*p3 = 30;  // 错误
p3 = &y;   // 错误

底层 const 与顶层 const

  • 底层 const:修饰指针指向的对象(如 const int* p),表示对象不可改;
  • 顶层 const:修饰指针本身(如 int* const p),表示指针不可改。
    这一区分在类型转换(如 const_cast)和函数参数传递中至关重要。
三、const 与引用:只读引用

引用本质是变量的别名,const 修饰引用(const T&)表示“只读引用”——不能通过引用修改被引用的对象。

核心特性:
  1. 不能通过引用修改对象

    int a = 10;
    const int& ref = a;
    ref = 20;  // 编译错误:assignment of read-only reference 'ref'
    a = 20;    // 合法:对象本身可改,仅引用限制修改
    
  2. 可绑定到临时对象或不同类型的对象
    const 引用(T&)只能绑定到同类型的非临时对象,而 const 引用(const T&)可以绑定到:

    • 同类型的 const/非 const 对象;
    • 临时对象(如表达式结果);
    • 隐式转换后与 T 兼容的对象。
    const int& ref1 = 10;  // 合法:绑定到临时对象(值 10)
    const int& ref2 = 3.14;  // 合法:double 隐式转换为 int 临时对象(值 3)
    

    这一特性使 const 引用成为函数参数的常用选择(扩大参数接受范围,避免拷贝)。

四、const 在类中的应用

类是 const 用法最复杂的场景,涉及数据成员、成员函数、静态成员等,核心是保护对象的“常量性”

1. const 数据成员

类的 const 数据成员必须在构造函数初始化列表中初始化,不能在构造函数体内赋值(因为进入函数体时成员已初始化)。

class A {
private:const int m_val;  // const 数据成员
public:// 必须在初始化列表中初始化A(int val) : m_val(val) {}  // 正确// A(int val) { m_val = val; }  // 错误:构造函数体内不能赋值
};
2. const 成员函数

在成员函数参数列表后加 const,表示该函数是“常量成员函数”,其核心约束是:不能修改对象的非静态数据成员,也不能调用非 const 成员函数

本质是因为 const 成员函数的 this 指针类型为 const T*(指向常量对象),而非 const 成员函数的 this 指针为 T*

class A {
private:int m_x;static int s_y;  // 静态成员(属于类,非对象)
public:// const 成员函数void print() const {// m_x = 10;  // 错误:不能修改非静态成员cout << m_x << endl;  // 合法:只读访问// setX(20);  // 错误:不能调用非 const 成员函数s_y = 30;  // 合法:可以修改静态成员(与对象无关)}// 非 const 成员函数void setX(int x) { m_x = x; }
};

const 对象与 const 成员函数的关系
const 对象只能调用 const 成员函数,非 const 对象可以调用任意成员函数:

const A a1(5);
a1.print();  // 合法:const 对象调用 const 函数
a1.setX(10);  // 错误:const 对象不能调用非 const 函数A a2(5);
a2.print();  // 合法:非 const 对象调用 const 函数
a2.setX(10);  // 合法
3. mutable 关键字:突破 const 成员函数的限制

mutable 修饰的成员变量可以在 const 成员函数中被修改,用于存储“逻辑上不影响对象常量性”的辅助数据(如缓存、计数器)。

class A {
private:mutable int m_count;  // 可在 const 函数中修改int m_val;
public:A(int val) : m_val(val), m_count(0) {}int getVal() const {m_count++;  // 合法:mutable 成员可修改return m_val;}
};
4. 静态 const 成员

类的静态 const 成员(static const T)是“类级别的常量”,所有对象共享,其初始化规则特殊:

  • 整数类型(intchar 等)可在类内直接初始化,也可类内声明+类外初始化;
  • 非整数类型(doublestring 等)必须类内声明,类外初始化。
class A {
public:// 整数类型:类内直接初始化static const int s_int = 10;// 非整数类型:类内声明static const double s_dbl;static const string s_str;
};// 非整数类型:类外初始化(必须在全局作用域)
const double A::s_dbl = 3.14;
const string A::s_str = "hello";

C++11 后可使用 constexpr static 简化非整数类型的初始化(类内直接初始化):

class A {
public:constexpr static double s_dbl = 3.14;  // 合法
};
五、const 与函数:参数和返回值

const 修饰函数参数或返回值时,用于限制参数的修改或返回值的使用方式。

1. const 函数参数
  • 值传递参数const 修饰值传递参数(如 void func(const int x))意义不大,因为参数是副本,修改副本不影响实参,const 仅限制函数内部修改副本。
  • 引用/指针参数const 修饰引用或指针参数(如 void func(const int& x)void func(const int* x))是核心用法,作用是:
    1. 防止函数内部修改实参;
    2. 允许参数接受 const 或非 const 类型的实参(扩大适用范围)。
void print(const string& s) {  // const 引用参数// s += " world";  // 错误:不能修改实参cout << s << endl;
}int main() {string s1 = "hello";const string s2 = "world";print(s1);  // 合法:非 const 实参print(s2);  // 合法:const 实参print("temp");  // 合法:绑定到临时对象return 0;
}
2. const 函数返回值
  • 返回值为对象const T func() 表示返回的对象是只读的,防止外部修改:
    const string getStr() { return "hello"; }
    int main() {getStr() += " world";  // 错误:返回的 const 对象不可修改return 0;
    }
    
  • 返回值为引用/指针const T& func()const T* func() 表示返回的引用/指针指向“只读数据”,常用于避免返回内部数据被外部修改:
    class Container {
    private:vector<int> m_data;
    public:const vector<int>& getData() const { return m_data; }  // 返回 const 引用
    };int main() {Container c;const vector<int>& data = c.getData();data.push_back(10);  // 错误:const 引用指向的数据不可修改return 0;
    }
    
3. const 与函数重载

const 可以作为函数重载的区分条件,主要体现在:

  • 成员函数是否为 constconst 成员函数与非 const 成员函数构成重载);
  • 指针/引用参数是否为 const(底层 const 差异可构成重载)。
class A {
public:// 重载:const 成员函数与非 const 成员函数void func() { cout << "non-const" << endl; }void func() const { cout << "const" << endl; }
};// 重载:const 引用参数与非 const 引用参数
void print(string& s) { cout << "non-const: " << s << endl; }
void print(const string& s) { cout << "const: " << s << endl; }int main() {A a;const A ca;a.func();   // 调用 non-const 版本ca.func();  // 调用 const 版本string s = "hello";const string cs = "world";print(s);   // 调用 non-const 参数版本print(cs);  // 调用 const 参数版本return 0;
}
六、const_cast:去除 const 限定

const_cast 是 C++ 四种类型转换运算符之一,用于去除指针或引用的 const 限定(只能操作指针或引用)。

用法与风险:
  • 若对象本身是 non-const,通过 const_cast 去除 const 后修改是合法的;
  • 若对象本身是 const,通过 const_cast 去除 const 后修改会导致未定义行为(编译器可能优化常量,修改后值不确定)。
// 情况 1:对象本身 non-const(合法)
int a = 10;
const int* p = &a;
int* np = const_cast<int*>(p);  // 去除 const
*np = 20;  // 合法,a 的值变为 20// 情况 2:对象本身 const(未定义行为)
const int b = 10;
const int* p2 = &b;
int* np2 = const_cast<int*>(p2);
*np2 = 20;  // 未定义行为:b 可能仍为 10(编译器优化)
七、constconstexpr 的区别

C++11 引入的 constexpr 常与 const 混淆,两者的核心差异是:

  • const 表示“只读”,值可以在运行时确定(如 const int x = get_val());
  • constexpr 表示“编译期常量”,值必须在编译时确定,可用于编译期计算(如数组大小、模板参数)。
const int x = 10;  // 可以是运行时常量
constexpr int y = 20;  // 必须是编译期常量int arr1[x];  // 合法(C++11 后 const 整数可作为数组大小)
int arr2[y];  // 合法int get_val() { return 30; }
const int z = get_val();  // 合法(运行时初始化)
// constexpr int w = get_val();  // 错误:get_val() 非 constexpr 函数,无法编译期求值

const 关键字通过编译期约束实现了“只读性”,其用法覆盖变量、指针、引用、类、函数等场景,核心价值在于:

  1. 安全性:防止意外修改数据,减少 bug;
  2. 可读性:明确标识“不可修改”的语义,便于代码理解;
  3. 灵活性:通过 const 引用/指针扩大函数适用范围,减少拷贝开销。

掌握 const 的关键是区分“被修饰的实体”(变量、指针、引用、函数)及其“不可修改的范围”,结合具体场景(如类的常量成员函数、函数重载)理解其约束逻辑。

http://www.dtcms.com/a/606836.html

相关文章:

  • 算法基础入门第一章
  • 家用除湿机方案开发,除湿机MCU控制方案设计
  • 搜狗推广做网站要钱吗wordpress首页调用指定文章
  • wordpress使用人数宁波seo在线优化公司
  • YOLO系列发展史与应用现状:从开山之作到实时目标检测的基石
  • 【电商微服务日志处理全方案】从MySQL瓶颈到大数据架构的实战转型
  • 蔬菜配送网站建设网络系统脆弱性的不安全因素
  • 常州想做个企业的网站找谁做注册公司需要交多少税
  • 反编译易语言程序 | 如何安全有效地进行易语言程序的反编译操作
  • 刺猬猫网站维护wordpress $post->id
  • 商城网站开发项目描述嘉兴建站公司
  • 从爆款到厂牌:解读游戏工业化的业务持续增长道路
  • 深度学习:学习率衰减(Learning Rate Decay)
  • 深度学习:RMSprop 优化算法详解
  • 盐城网站建设费用怎么判断一个网站是否使用帝国做的
  • 企业电子商务网站设计的原则做旅游宣传哪个网站好
  • 进程程序替换函数(Linux)
  • [特殊字符] 莫生指纹浏览器 v1.0.1 - 专业的浏览器指纹管理工具
  • 广州网站运营专业乐云seo58网络门店管理系统
  • 4399网站开发姜堰网站定制
  • Oracle 基础入门:核心概念与实操指南(视频教程)
  • Kafka 消费积压影响写入?试试 Pulsar
  • 遂溪网站开发公司js 访问wordpress
  • 电容上产生的寄生电感的主要原因有哪些?
  • 门户网站 建设 投入wordpress视频教程百度云
  • 上海手机站网站建设数据库在网站建设中的作用
  • 网站建设的域名续费网址提交
  • 编译语言 | 探索不同编程语言的编译过程与效率
  • 孤能子视角:中西文明认知模式分析,外观与内理(2)
  • 襄樊大型网站建设南京做网站优化