深入理解 C++ const:九大用法解析 + 八大高频考点

一、const 修饰普通变量

const 修饰的变量不可修改(常量),必须在定义时初始化:

const int x = 10;  // x 不可修改
x = 20;            // 编译错误!

二、const 修饰指针

2.1、指向常量的指针(指针指向的内容不可变)

不能通过该指针修改其指向的变量,又叫底层 const

int a = 10, b = 20;
const int* ptr = &a; // 或 int const* ptr;
// *ptr = 30;        // 错误!不能通过ptr修改a的值
a = 30;              // 合法,直接修改a
ptr = &b;            // 合法,可以改变指针指向

2.2、常量指针(指针本身不可变)

指针一旦初始化后就不能再指向其他地址,可以通过该指针修改它所指向的值。又叫顶层const

int a = 10, b = 20;
int* const ptr = &a;
*ptr = 30;    // 合法,可以修改指向的值
// ptr = &b;  // 错误!不能改变指针指向

2.3、指向常量的常量指针(指针和指向的内容都不可变)

指针不能改变指向,也不能通过该指针修改它所指向的值

int a = 10, b = 20;
const int* const ptr = &a;
// *ptr = 30;  // 错误!不能修改指向的值
// ptr = &b;   // 错误!不能改变指针指向
a = 30;       // 合法,直接修改a

三、const 修饰引用

const 引用绑定后不能通过引用修改原变量

int x = 10;
const int& ref = x;
// ref = 20;        // 编译错误!

四、const 修饰函数形参

const 修饰函数参数,防止参数在函数内被修改

void print(const std::string& str) {
    // str[0] = 'X';  // 编译错误!
    std::cout << str << std::endl;
}

五、const 修饰函数返回值

const 修饰返回值时,表示返回值不可被修改

const int* getPointer() {
    static int x = 10;
    return &x;
}
// *(getPointer()) = 20;  // 编译错误!

六、const 修饰成员变量

表示该成员变量在对象生命周期内不可被修改,必须在构造函数的初始化列表中进行初始化,每个类实例都有自己的 const 成员变量副本,这点与 static 修饰的成员变量不一致,静态成员变量是类的实例共享的。

class MyClass {
public:
    const int value;
    MyClass(int v) : value(v) {}  // 必须在初始化列表中初始化
};

不能在构造函数内赋值

class Example {
    const int x;
public:
    Example() { x = 10; }  // 错误!不能在构造函数体内赋值
};

七、const 修饰成员函数

const 修饰成员函数时,表示该函数不会修改类的成员变量(除非成员变量被 mutable 修饰)

class MyClass {
public:
    int a;
    mutable int b;

    MyClass(int x, int y): a(x), b(y) {}

    int getA() const { return a; }
    int getB() const { return b; }
    // void setA(int v) const { a = v; }  // 编译错误!
    void setB(int v) const { b = v; }
};

int main() {
    MyClass myClass(1, 2);
    myClass.setB(3);
    std::cout << myClass.getA() << std::endl;
    std::cout << myClass.getB() << std::endl;
    return 0;
}
  • const对象只能调用const成员函数
  • const对象优先调用非const版本,也可调用const版本

八、const 修饰类对象

const 类对象只能调用 const 成员函数(对象状态不可变)

class MyClass {
public:
    int a;
    mutable int b;

    MyClass(int x, int y): a(x), b(y) {}

    int getA() const { return a; }
    int getB() const { return b; }
    // void setA(int v) const { a = v; }  // 编译错误!
    void setB(int v) const { b = v; }
};

int main() {
    const MyClass myClass(1, 2);
    myClass.setB(3);
    std::cout << myClass.getA() << std::endl;
    std::cout << myClass.getB() << std::endl;
    return 0;
}

九、static const 成员变量

static 特性:

  • 属于类而非对象(所有对象共享同一份拷贝)
  • 生命周期贯穿整个程序运行期间
  • 需要在类外单独定义(C++17 前)

const 特性:

  • 值不可以修改
  • 必须在定义时初始化

9.1、C++11 前的 static const

class MyClass {
public:
    static const int a = 100;   // 声明并初始化(仅整型和枚举类型允许)
    static const double b;      // 非整型必须类外定义
    static const std::string c; // 非整型必须类外定义
};

const int MyClass::a;                     // C++11 之前 int 也需要类外定义
const double MyClass::b = 100.100;        // 非整型必须在类外定义并初始化
const std::string MyClass::c = "hahaha";  // 非整型必须在类外定义并初始化

int main() {
    std::cout << MyClass::a << std::endl;
    std::cout << MyClass::b << std::endl;
    std::cout << MyClass::c << std::endl;
    return 0;
}

9.2、C++11 的 static const

C++11 引入 constexpr 关键字,可以直接在类内初始化

class MyClass {
public:
    static const int a = 100;
    static constexpr double b = 10.0;
    static constexpr const char* c = "hahaha";  // 字符串常量
};

int main() {
    std::cout << MyClass::a << std::endl;
    std::cout << MyClass::b << std::endl;
    std::cout << MyClass::c << std::endl;
    return 0;
}

9.3、C++17 的 static const

C++17 引入了 inline 的其他用法,用于消除 ORD 问题

在 C++17 前,static const 成员变量需要在类外单独定义(通常在 .cpp 文件中),使用 inline 后可以完全在类定义内完成:

class MyClass {
public:
    inline static const int a = 100;
    inline static constexpr double b = 10.0;
    inline static constexpr const char* c = "hahaha";  // 字符串常量
};

int main() {
    std::cout << MyClass::a << std::endl;
    std::cout << MyClass::b << std::endl;
    std::cout << MyClass::c << std::endl;
    return 0;
}

十、考点

10.1、const 可以修饰局部变量嘛?

可以,全局 const 变量通常存储在只读数据段,而局部 const 变量通常存储在栈上(但编译器可能优化)。在C中,const 局部变量不是真正的常量(不能用于数组大小等需要常量表达式的场合),而在C++中,const 局部变量可以用于常量表达式(如果使用常量表达式初始化)。

10.2、const 成员函数和普通成员函数可以重载嘛?

可以,const修饰的成员函数可以与普通成员函数构成重载,因为const成员函数实际上具有不同的函数签名。

class MyClass {
public:
    // 普通成员函数
    void display() { std::cout << "Non-const display" << std::endl; }
    
    // const成员函数(构成重载)
    void display() const { std::cout << "Const display" << std::endl; }
    
    // 可以修改成员变量
    void modify() { value = 10; }
    
    // 不能修改成员变量
    void readOnly() const {
        // value = 20; // 错误!const成员函数不能修改成员变量
        std::cout << value << std::endl;
    }
    
    int value = 0;
};

int main() {
    MyClass obj1;          // 非const对象
    const MyClass obj2;    // const对象
    
    obj1.display();   // 调用非const版本
    obj2.display();   // 调用const版本
    obj1.modify();    // 合法
    // obj2.modify(); // 错误!const对象不能调用非const成员函数
    obj1.readOnly();  // 合法,非const对象可以调用const成员函数
    obj2.readOnly();  // 合法
}

10.3、下面这个成员函数是什么意思

class DataContainer {
public:
    std::vector<int> data;

    const int* getData() const {
        return data.empty() ? nullptr : &data[0];
    }
};
  • 第一个const 修饰返回值(返回的指针不能用于修改指向的内容)
  • 第二个const 修饰成员函数(不能修改成员变量)

所以这个成员函数的两个 const 修饰的意义是不一样的。

10.4、const#define 的区别

特性const#define
调试支持可调试不可调试
内存分配分配内存不分配内存
类型检查
作用域有作用域无作用域(全局)
是否可取消不可取消可通过#undef取消

10.5、const 与引用的关系

常量引用:

int y = 30;
const int &cr = y;  // 常量引用
// cr = 40;         // 错误!不能通过cr修改y
y = 40;             // 但可以直接修改y

引用常量:

int z = 30;
int& const cr = z;  // 错误引用本身就不能重新绑定

这么写是没有意义的,因为引用本身就不能重新绑定,只能在初始化的时候绑定一次,所以之前也有一种说法:引用的底层就是指针常量

10.6、以下代码哪些是错误的?为什么?

const int a = 10;
int *p = &a;              // 错误,普通指针不能指向const变量
const int *p2 = &a;       // 正确
int* const p3 = &a;       // 错误,同第一个
const int* const p4 = &a; // 正确

10.7、为什么static成员函数不能是const

因为static成员函数没有this指针,不属于特定对象,而const是承诺不修改所属对象。

10.8、总结

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LyaJpunov

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值