C++ 中的 static 关键字:类成员、局部变量与单例模式

在前面我们聊过 C++ 的函数、引用、类与结构体。这次我们来看看一个常常让初学者迷惑的关键字:static

它的作用主要有三类:

  1. 类/结构体中的静态成员(全体实例共享)

  2. 局部静态变量(作用域局部,生命周期全局)

  3. 单例模式(利用静态变量确保唯一实例)


1. 类/结构体中的静态成员

先看一个例子:

cpp 复制代码
#include <iostream>

struct Entity
{
    int x, y;

    void Print() {
        std::cout << x << ", " << y << std::endl;
    }
};

int main()
{
    Entity e;
    e.x = 2;
    e.y = 3;

    Entity e1 = {5, 8};

    e.Print();   // 2, 3
    e1.Print();  // 5, 8
}

这时 ee1 各自有独立的 xy


如果我们把它们改成静态的:

cpp 复制代码
struct Entity
{
    static int x, y;
};

编译时会报错:

cpp 复制代码
error LNK2001: 无法解析的外部符号 "public: static int Entity::x"

这是因为 静态成员变量需要在类外部进行定义,否则编译器不知道为它们分配存储空间:

cpp 复制代码
int Entity::x;
int Entity::y;

这样就能正常使用了。

静态成员的特性

  • 静态成员变量在 所有实例中只有一份拷贝

  • 无论通过哪个对象修改它,所有对象看到的都是同一个值。

  • 使用时推荐直接写 Entity::x,而不是 e.x,因为它本质上不属于某个实例。

cpp 复制代码
Entity e;
Entity::x = 2;
Entity::y = 3;

Entity e1;
Entity::x = 5;
Entity::y = 8;

std::cout << Entity::x << ", " << Entity::y;  // 输出 5, 8

这就像 Entity 类是一个"命名空间",xy 属于它本身,而不是对象。


静态方法

静态方法也是类似的:

cpp 复制代码
struct Entity
{
    static void Print() {
        std::cout << "Hello Entity" << std::endl;
    }
};

调用时不需要实例:

cpp 复制代码
Entity::Print();

注意:静态方法不能访问非静态成员,因为它没有绑定到某个对象。


2. 局部 static 变量

static 不仅能用在类里,也能用在函数里:

cpp 复制代码
void Function()
{
    static int i = 0;
    i++;
    std::cout << i << std::endl;
}

和普通局部变量的区别在于:

  • 普通局部变量:每次进入函数都会重新创建,退出函数就销毁。

  • 静态局部变量:只会在第一次调用函数时初始化一次,之后一直存在,直到程序结束。

运行:

cpp 复制代码
for (int j = 0; j < 5; j++)
    Function();

输出:

cpp 复制代码
1 2 3 4 5

这说明 i 在函数多次调用之间是共享的。

作用域与生命周期

  • 作用域:只在函数内可见。

  • 生命周期:整个程序。

这让它既能避免全局变量的污染,又能保持状态。


3. 单例模式(Singleton)

单例模式指的是 一个类在整个程序中只能有一个实例

在 C++ 中可以用 static 很方便地实现。

单例模式(Singleton)详解

为什么需要单例?

假设你要做一个游戏引擎,里面有个日志系统 Log

你希望:

  • 全局只有一个日志对象

  • 任何地方都能访问它

  • 不想每次用日志都要 new 一个对象

如果每次都写:

cpp 复制代码
Log log1;
Log log2;

就可能有多个实例,日志配置不一致,管理起来很乱。

这时就希望**"某个类在全局只有一个实例"** ------ 这就是单例模式。

单例模式的核心思想:

自己管住自己,不让别人随便创建对象,只暴露一个获取实例的方法。

  • 构造函数设为私有(private) → 别人不能随便 new

  • 类里放一个 static 实例 → 全局只有一份

  • 提供一个静态方法 Get() → 外部只能通过它来拿到这个实例

写法 1:类静态指针
cpp 复制代码
#include <iostream>

class Singleton
{
private:
    // 1. 构造函数私有化
    Singleton() {}

    // 2. 静态指针,存放唯一实例
    static Singleton* s_Instance;

public:
    // 3. 静态方法,外部通过它获取实例
    static Singleton& Get()
    {
        if (s_Instance == nullptr)
            s_Instance = new Singleton(); // 第一次用时才创建
        return *s_Instance;
    }

    void Hello()
    {
        std::cout << "Hello from Singleton!" << std::endl;
    }
};

// 类外初始化静态成员
Singleton* Singleton::s_Instance = nullptr;

int main()
{
    // 获取唯一实例
    Singleton& s1 = Singleton::Get();
    Singleton& s2 = Singleton::Get();

    s1.Hello();
    s2.Hello();

    std::cout << &s1 << std::endl;
    std::cout << &s2 << std::endl; // 地址一样 → 确实只有一个对象
}

调用:

cpp 复制代码
Singleton::Get().Hello();


//运行结果
Hello from Singleton!
Hello from Singleton!
0x600003e00
0x600003e00

写法 2:局部静态变量(推荐)
cpp 复制代码
class Singleton
{
public:
    static Singleton& Get()
    {
        static Singleton instance; // 第一次调用时创建,以后一直用这一个
        return instance;
    }

    void Hello()
    {
        std::cout << "Hello from Singleton!" << std::endl;
    }

private:
    Singleton() {} // 构造函数私有化
};

调用:

cpp 复制代码
Singleton::Get().Hello();

//运行结果
Hello from Singleton!
Hello from Singleton!
0x600003e00
0x600003e00

特点:

  • 只会创建一次(第一次调用时)

  • 线程安全(C++11 起保证了 static 局部变量初始化是线程安全的)

  • 自动销毁(程序结束时,编译器会释放它)


相关推荐
小杨同学yx19 分钟前
有关maven的一些知识点
java·开发语言
我是华为OD~HR~栗栗呀33 分钟前
华为od-21届考研-C++面经
java·c语言·c++·python·华为od·华为·面试
oioihoii41 分钟前
C++ 中的类型转换:深入理解 static_cast 与 C风格转换的本质区别
java·c语言·c++
重生之我要当java大帝42 分钟前
java微服务-尚医通-编写医院设置接口下
java·开发语言·sql
寻找华年的锦瑟42 分钟前
Qt-QDir
开发语言·qt
小妖6661 小时前
vscode 怎么运行 c++ 文件
开发语言·c++
weixin_307779131 小时前
在 Microsoft Azure 上部署 ClickHouse 数据仓库:托管服务与自行部署的全面指南
开发语言·数据库·数据仓库·云计算·azure
鱼鱼说测试1 小时前
Linux下运行Jmeter
开发语言·python
lingran__1 小时前
算法沉淀第三天(统计二进制中1的个数 两个整数二进制位不同个数)
c++·算法
鱼鱼说测试1 小时前
postman功能接口测试
开发语言·lua