C++之const和static的使用及说明

 更新时间:2025年09月18日 08:56:38   作者:EnigmaCoder  
C++中const与static的用法:const限制变量/对象只读性,确保数据安全;static控制变量/函数的存储周期与作用域,实现数据共享,两者结合的static const用于定义类级常量,需注意const变量不可强制修改、static局部变量线程安全问题及类static成员必须类外初始化的要点

本文将分别介绍const与static的用法,最后介绍两者的结合应用和常见坑点。

const:“只读”的守护者

const 的核心作用是限制变量/对象的“可修改性”,强制编译器检查“意外修改”,本质是给代码加“安全锁”。

它的用法围绕“修饰谁”展开,不同修饰对象的含义完全不同,也是初学者最易踩坑的点。

修饰普通变量

  • 作用:变量初始化后不可修改,编译期会检查赋值操作。
  • 注意:必须在定义时初始化(局部/全局 const 均需)。

示例:

const int max_len = 10; // 正确:定义时初始化
max_len = 20; // 错误:编译器直接报错,禁止修改

修饰指针

const修饰指针关键看 const* 的位置:

  • const int* pconst* 左边):限制“指针指向的内容”不可改,指针本身可以换指向。
int a = 5, b = 10;
const int* p = &a;
*p = 6; // 错误:指向的内容(a的值)不能改
p = &b;  // 正确:指针可以指向新地址(b)
  • int* const pconst* 右边):限制“指针本身”不可改,指向的内容可以改。
int a = 5, b = 10;
int* const p = &a;
*p = 6; // 正确:指向的内容(a的值)可以改
p = &b;  // 错误:指针不能换指向

修饰函数

修饰函数参数:保证函数内部不修改参数(尤其针对引用/指针参数,避免意外篡改外部变量)。

// 传入字符串,仅读取不修改,用 const 保护
void printStr(const string& s) {
    s += "test"; // 错误:禁止修改 const 参数
    cout << s << endl; // 正确:仅读取
}

修饰函数返回值:限制返回值不可被修改(常见于返回指针/引用的场景,避免外部篡改内部数据)。

// 返回数组首地址,禁止外部修改数组内容
const int* getArr() {
    static int arr[3] = {1,2,3};
    return arr;
}
int main() {
    const int* p = getArr();
    *p = 4; // 错误:返回值是 const,禁止修改
}

修饰类成员

const 成员变量:属于对象的“常量”,必须在构造函数初始化列表中初始化(不能在定义时直接赋值)。

class Person {
private:
    const int age; // const 成员变量
public:
    // 正确:在初始化列表中初始化
    Person(int a) : age(a) {}
    // 错误:不能在构造函数体内赋值
    // Person(int a) { age = a; }
};

const 成员函数:保证函数内部不修改任何非 mutable 成员变量,函数声明和定义都要加 const

class Person {
private:
    int age;
public:
    // 声明时加 const
    void showAge() const;      //语法:返回值类型 函数名() const;
};
// 定义时也要加 const(位置不能错)
void Person::showAge() const {
    age = 20; // 错误:const 函数禁止修改成员变量
    cout << age << endl; // 正确:仅读取
}

修饰对象

const 修饰对象,本质是把对象变成 “只读对象”—— 一旦定义,这个对象的成员变量就不能被修改,能有效保证数据安全(比如防止误改关键数据)。

  • 格式很简单:在对象定义前加const,和定义const变量(比如 const int a=10)的逻辑一致。
#include <iostream>
#include <string>
using namespace std;

// 定义一个学生类
class Student {
public:
    string name;  // 成员变量:姓名
    int score;    // 成员变量:成绩

    // 构造函数(初始化姓名和成绩)
    Student(string n, int s) : name(n), score(s) {}

    // 普通成员函数:修改成绩(可能改成员变量)
    void setScore(int s) {
        score = s;
    }

    // const 成员函数:只打印信息(不修改成员变量)
    void showInfo() const {
        cout << "姓名:" << name << ",成绩:" << score << endl;
    }
};
int main() {
    // 1. 普通对象(可以修改成员,调用任意函数)
    Student s1("张三", 90); 
    s1.score = 85;          // 允许:普通对象能改成员变量
    s1.setScore(88);        // 允许:普通对象能调用非const函数
    s1.showInfo();          // 允许:普通对象也能调用const函数

    // 2. const对象(只读,核心限制:不能改成员,只能调const函数)
    const Student s2("李四", 85); 
    // s2.score = 90;       // 错误!const对象不能直接修改成员变量
    // s2.setScore(92);     // 错误!const对象不能调用非const函数
    s2.showInfo();          // 允许!const对象只能调用const成员函数

    return 0;
}

遵循两个规则:

  • const对象的成员变量绝对不能修改。
  • const对象只能调用const成员函数。

static:“静态存储”与“作用域控制”

static 的核心是改变变量/函数的“存储周期”或“作用域”,本质是管理“数据的生命周期”和“访问范围”,常见于“共享数据”“全局唯一”场景。

修饰全局变量

  • 作用:全局 static 变量的作用域仅限当前 .cpp 文件,避免不同文件中“同名全局变量”的命名冲突。
  • 对比普通全局变量:普通全局变量默认“跨文件可见”(用 extern 可引用),static 全局变量则“文件私有”。

示例:

// a.cpp
static int global_val = 5; // 仅 a.cpp 可见

// b.cpp
extern int global_val; // 错误:无法引用 a.cpp 的 static 全局变量

修饰局部变量

  • 作用:局部 static 变量的“作用域”仍在函数内,但“存储周期”是整个程序(仅初始化一次,函数调用结束后不销毁)。
  • 典型场景:函数内的计数器、全局唯一的临时数据。

示例:

void countCall() {
    static int cnt = 0; // 仅初始化一次(第一次调用时)
    cnt++;
    cout << "调用次数:" << cnt << endl;
}
int main() {
    countCall(); // 输出 1
    countCall(); // 输出 2(cnt 未被销毁)
}

修饰类成员

static 类成员是“所有对象共享的数据/方法”,不依赖具体对象存在。

static 成员变量

  • 特点:属于整个类,所有对象共用同一内存,必须在类外单独初始化(不能在构造函数中初始化)。由于static成员变量存储在类的外部,计算类的大小时不包含在内。

示例:统计类的对象创建个数

class Student {
private:
    static int total; // 声明:属于 Student 类
public:
    Student() { total++; } // 创建对象时计数+1
    ~Student() { total--; } // 销毁对象时计数-1
    // 必须用 static 函数访问 static 变量(见下文)
    static int getTotal() { return total; }
};

// 类外初始化:不加 static,需指定类名
int Student::total = 0;

int main() {
    Student s1, s2;
    cout << Student::getTotal(); // 输出 2(两个对象)
}

static成员函数

  • 特点:没有 this 指针(因为不依赖对象),只能访问 static 成员变量/函数,不能访问非 static 成员。
  • 调用方式:直接用“类名::函数名”调用(无需创建对象)。
  • 示例:上文 Student::getTotal(),直接通过类名调用。

const 与 static 结合应用

两者结合的核心场景是“类的静态常量”,即 static const(或 const static,两者在类中等价),用于定义“类级别的常量”(所有对象共用,且不可修改),既实现了数据共享又达到了数据不可被改变的目的。

关键用法:类内静态常量的初始化

C++11 及以后:static const 成员变量可在类内直接初始化(仅限字面量类型,如 int、char 等)。

示例:

class Config {
public:
    // C++11+ 支持:类内直接初始化静态常量
    static const int MAX_NUM = 100;
    static const string DEFAULT_NAME; // 非字面量类型,需类外初始化
};

// 非字面量类型的 static const 成员,仍需类外初始化
const string Config::DEFAULT_NAME = "unknown";

注意:C++11 前,static const 成员变量必须在类外初始化,即使是字面量类型。

避坑指南

坑 1:const 变量能“强制修改”?

用指针强制转换可以修改 const 变量,但这是“未定义行为”(编译器不报错,但运行结果不可控,可能触发崩溃),绝对禁止!

const int a = 5;
int* p = (int*)&a; // 强制转换(危险!)
*p = 10; // 运行时可能修改成功,但属于未定义行为

坑 2:static 局部变量的线程安全?

  • C++11 及以后:static 局部变量的初始化是“线程安全”的(编译器会加锁,避免多线程同时初始化)。
  • C++11 前:非线程安全,多线程同时调用可能导致重复初始化。

坑 3:类 static 成员忘记类外初始化?

仅在类内声明 static 成员变量,未在类外初始化,会导致链接错误(编译器找不到变量的定义)。记住:static 类成员“声明在类内,定义在类外”。

结尾总结

conststatic 虽基础,但用法灵活,核心记住两点:

  • const:围绕“只读”,为代码加安全锁,避免意外修改(重点分清修饰指针的两种场景、类 const 成员的初始化)。
  • static:围绕“静态存储/作用域”,管理数据生命周期(重点掌握类 static 成员的“类外初始化”和“共享特性”)。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • C语言内存泄露很严重的解决方案

    C语言内存泄露很严重的解决方案

    这篇文章主要介绍了C语言内存泄露很严重的解决方案,预防内存泄漏问题有多种方法,比如加强代码检视、工具检测和内存测试等,下面文章总结内容需要的小伙伴可以参考一下
    2022-05-05
  • C语言非递归后序遍历二叉树

    C语言非递归后序遍历二叉树

    这篇文章主要为大家详细介绍了C语言非递归后序遍历二叉树,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10
  • 浅谈分词器Tokenizer

    浅谈分词器Tokenizer

    分词器的工作就是分解文本流成词(tokens).在这个文本中,每一个token都是这些字符的一个子序列。一个分析器(analyzer)必须知道它所配置的字段,但是tokenizer不需要,分词器(tokenizer)从一个字符流(reader)读取数据,生成一个Token对象(TokenStream)的序列
    2021-06-06
  • C语言中单链表的基本操作指南(增删改查)

    C语言中单链表的基本操作指南(增删改查)

    链表跟数组不同的是非连续存储结构,也就是说实现链表需要一个指针,每用完一个节点指针指向下一个节点,直至表尾,下面这篇文章主要给大家介绍了关于C语言中单链表的基本操作之增删改查的相关资料,需要的朋友可以参考下
    2021-09-09
  • 如何在C++中实现一个正确的时间循环器详解

    如何在C++中实现一个正确的时间循环器详解

    这篇文章主要给大家介绍了关于如何在C++中实现一个正确的时间循环器的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • C语言详解判断相同树案例分析

    C语言详解判断相同树案例分析

    这篇文章主要介绍了用C语言检查两棵树是否相同,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2022-04-04
  • C/C++实现的游戏角色名称名字随机生成代码

    C/C++实现的游戏角色名称名字随机生成代码

    这篇文章主要介绍了C/C++实现的游戏角色名称名字随机生成代码,本文特别针对一些古典游戏的角色名称进行随机生成,需要的朋友可以参考下
    2015-05-05
  • C语言的各类变量和零值的比较详解

    C语言的各类变量和零值的比较详解

    这篇文章主要为大家介绍了C语言的各类变量和零值的比较,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-11-11
  • 一篇带你了解C语言--位操作详情

    一篇带你了解C语言--位操作详情

    这篇文章主要介绍了关于C语言位运算的简单示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-08-08
  • 一篇文章带你用C语言玩转结构体

    一篇文章带你用C语言玩转结构体

    本文主要介绍C语言 结构体的知识,学习C语言肯定需要学习结构体,这里详细说明了结构体并附示例代码,供大家参考学习,有需要的小伙伴可以参考下
    2021-09-09

最新评论