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 局部变量初始化是线程安全的)

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


相关推荐
天將明°4 小时前
单例模式指南:全局资源的安全访问
c语言·单例模式·设计模式
yujkss4 小时前
23种设计模式之【单例模式模式】-核心原理与 Java实践
java·单例模式·设计模式
know__ledge4 小时前
Pytest+requests进行接口自动化测试5.0(5种assert断言的封装 + pymysql)
服务器·开发语言·python·测试用例·pytest
golang学习记4 小时前
从0死磕全栈第十天:nest.js集成prisma完成CRUD
开发语言·javascript·jvm
Familyism4 小时前
Java虚拟机——JVM
java·开发语言·jvm
Biomamba生信基地5 小时前
挑战用R语言硬干一百万单细胞数据分析
开发语言·数据分析·r语言·生信·医药
烈风5 小时前
009 Rust函数
java·开发语言·rust
coding-fun5 小时前
SuperScript:C#脚本编辑器、C#脚本引擎
开发语言·c#·编辑器
研來如此5 小时前
现有项目添加CMake
开发语言