一、基础定义

class Person {
public:
    // 普通成员函数
    void show() {
        age = 100; // 可以修改成员变量
    }
    // 常量成员函数:函数括号后加 const
    void print() const {
        // age = 100; // 编译报错,禁止修改成员变量
    }
private:
    int age = 18;
};

语法规则:写在 () 之后、{} 之前,修饰整个成员函数,称为常量成员函数(const member function)

二、底层原理:this 指针被 const 修饰

成员函数内部隐含 this 指针:

  1. 普通成员函数:Person* const this

    1. this 本身不可改,但可以通过this->修改成员变量

  2. const 成员函数:const Person* const this

    1. this 指针指向的对象是常量对象,不能通过 this 修改任何成员

// void print() const
void print(const Person* const this) {
    this->age = 20; // 非法,*this 是 const
}

三、三大核心作用

1. 限制函数内不能修改类的成员变量

const 成员函数内:

  • 不能给普通成员赋值

  • 不能调用非 const 成员函数(非 const 函数可能修改对象,不允许)

  • 可以读取成员变量、调用其他 const 成员函数

void print() const {
    age = 30; // 报错:const函数不能修改成员
    show();   // 报错:show是非const函数,禁止调用
}

2. 常量对象只能调用 const 成员函数

const修饰的对象(常量对象),语法强制只能调用带 const 的成员函数:

int main() {
    const Person p; // 常量对象
    p.print(); // 合法,print是const函数
    p.show();  // 编译报错,show无const,可能修改常量对象
    return 0;
}

这是最常用场景:保证常量对象不会被意外修改。

3. 提升代码可读性、规范语义

标记该函数只读、不会改动对象状态,阅读代码一眼就能区分:

  • 无 const:读写函数,可能修改成员

  • 尾部 const:纯查询函数,只读取数据

四、特殊情况:mutable 突破 const 限制

如果某个成员变量需要在 const 函数中修改,加mutable关键字修饰成员:

class Person {
public:
    void print() const {
        cnt++; // 合法,mutable变量不受const函数约束
    }
private:
    int age = 18;
    mutable int cnt = 0; // 可变成员,日志/计数器常用
};

适用场景:缓存、访问计数、调试标记等不影响对象核心状态的变量。

五、重载:const 与非 const 构成函数重载

同一个函数名可以同时写 const 和非 const 两个版本,编译器自动匹配调用:

  • 普通对象 → 优先调用非 const 版本

  • const 对象 /const 引用对象 → 调用const 版本

#include <iostream>
using namespace std;

class Person {
public:
    string& getName() {
        cout << "调用非const版本" << endl;
        return name;
    }
    const string& getName() const {
        cout << "调用const版本" << endl;
        return name;
    }
private:
    string name = "小明";
};

int main() {
    Person p;
    p.getName(); // 非const对象 → 非const版本

    const Person cp;
    cp.getName(); // const对象 → const版本
    return 0;
}

输出:

调用非const版本
调用const版本

六、返回值搭配 const 的配套规范

const 成员函数一般返回const 引用,防止外部拿到引用后修改内部成员:


// 规范写法 const string& getName() const { return name; } // 错误:返回普通引用,外部可修改私有成员 string& getName() const { return name; }

七、完整可运行综合示例

#include <iostream>
#include <string>
using namespace std;

class Student {
public:
    Student(string n, int a) : name(n), age(a) {}

    // 非const函数:可修改成员
    void setAge(int a) {
        age = a;
    }

    // const成员函数:只读,不能改成员
    void info() const {
        // setAge(20); // 报错,禁止调用非const函数
        cout << "姓名:" << name << " 年龄:" << age 
             << " 访问次数:" << visitCnt << endl;
        visitCnt++; // mutable允许修改
    }

private:
    string name;
    int age;
    mutable int visitCnt = 0; // 计数器,mutable突破const
};

int main() {
    Student s("张三", 18);
    s.info();
    s.setAge(20);
    s.info();

    const Student cs("李四", 19);
    cs.info(); // const对象只能调用info() const
    // cs.setAge(22); // 编译报错
    return 0;
}

运行输出:

姓名:张三 年龄:18 访问次数:0
姓名:张三 年龄:20 访问次数:1
姓名:李四 年龄:19 访问次数:0

八、常见误区总结

  1. 误区:const 修饰返回值

    1. 错:const void func() 修饰返回值

    2. 对:void func() const 修饰 this 指针、对象只读

  2. 误区:const 函数完全不能修改任何变量

    1. 错:mutable 修饰的成员可以修改

  3. 误区:常量对象能调用普通成员函数

    1. 错:常量对象仅允许调用尾部带 const 的成员函数

  4. 误区:全局 / 静态函数能加尾部 const

    1. 错:只有普通类成员函数可以加,静态成员无 this 指针,不能写尾部 const

九、总结

  1. 本质:函数内this变为const Person* const,禁止修改对象成员;

  2. 权限约束:const 对象只能调用 const 成员函数;

  3. 语义区分:标记函数为只读查询接口,代码更安全规范;

  4. 扩展:mutable 可让特定成员在 const 函数内修改;const / 非 const 可成对重载适配不同对象。

补充:和之前流运算符知识点联动

重载operator<<时,自定义类输出函数为友元全局函数;如果类内部有读取私有成员的查询接口,建议全部写成尾部 const 成员函数,保证常量对象也能正常输出。

// 配合const成员函数的流重载
class Student {
public:
    int getAge() const { return age; } // const查询接口
private:
    int age = 18;
    friend ostream& operator<<(ostream& os, const Student& s);
};
ostream& operator<<(ostream& os, const Student& s) {
    os << s.getAge(); // 传入const引用,只能调用const成员
    return os;
}

更多推荐