静态成员变量为什么只能在类外初始化?如何手动控制静态成员变量的初始化时机?

静态成员变量,也能称为类变量,它们需要在类的定义外部进行初始化,因为静态成员变量属于整个类,而不是类的特定实例。这与普通的实例变量有所不同,实例变量是每个类的实例独有的,因此可以在构造函数中或者原位初始化,但是他们的初始化时机都是在构造一个实例化对象时。

以下是静态成员变量要在类外初始化的原因:

  • 内存分配: 静态成员变量是类的所有实例共享的,它们存储在类的静态存储区域,而不是实例的堆栈区域。因此,在 编译阶段 就需要为它们分配内存空间,以便在整个程序生命周期内都可以使用。

  • 唯一性: 静态成员变量是类级别的,它们在所有类实例之间是唯一的。如果允许在类内部初始化,就可能会导致在每个实例中都有一个独立的副本(每实例化一个对象时,成员变量都会进行一次初始化(或于构造函数中,或于原位进行初始化),因此会造该静态变量成每个实例不同),这与静态成员变量的目标相悖。

  • 避免多次初始化: 如果静态成员变量允许在类内部初始化,每个编译单元(源文件)都可能有自己的初始化值。这会导致在链接多个编译单元时出现问题,因为无法确定哪个初始化值应该是正确的。

因此,为了确保静态成员变量在整个程序中都有唯一的、确定的初始化值,必须在类的定义外部进行初始化。通常,在类的实现文件(.cpp 文件)中进行初始化是一个常见的做法。这样,每个编译单元只会看到一份初始化值,确保了一致性。

以下是一个简单的示例,展示了如何在类外部初始化静态成员变量:

// MyClass.h 头文件
class MyClass {
public:
    static int staticVariable; // 在头文件中声明静态成员变量
};

// MyClass.cpp 实现文件
#include "MyClass.h"
int MyClass::staticVariable = 42; // 在实现文件中初始化静态成员变量

在这个示例中,静态成员变量 staticVariable在类外部 被初始化为 42。这样,在整个程序中,所有使用 MyClass::staticVariable 的地方都会看到同样的值。


能否手动控制静态成员变量的初始化时机呢?

—— 不能,但可以假初始化

在一般情况下,静态成员变量的初始化时间是在程序启动阶段,即在 main 函数执行之前。这是因为静态成员变量是在编译期间分配内存的,并且其初始化代码会在程序加载时执行。

虽然一般情况下无法直接控制静态成员变量的初始化时间,但是可以通过一些技巧和编程模式来实现类似的效果。以下是一些方法:

  • 懒汉式初始化:先在类外给他假初始化为nullptr,将真正的初始化操作 延迟到第一次访问时。这通常使用一个 静态成员函数 来实现,该函数在首次调用时初始化静态成员变量并返回其值。

    #include <iostream>
    #include <memory>
    
    class LazyResourceLoading {
    public:
        static std::shared_ptr<Model> GetResource() {
            if (!resource) {
                LoadResource();
            }
            return resource;
        }
    
    private:
        static std::shared_ptr<Model> resource;
    
        static void LoadResource() {
            resource = std::shared_ptr<Model>(......); // 模拟加载资源
            std::cout << "Resource loaded." << std::endl;
        }
    };
    // 初始化为nullptr,这样程序启动之前并不会执行加载操作
    std::shared_ptr<Model> LazyResourceLoading::resource = nullptr;
    
    int main() {
        std::cout << "Before accessing resource." << std::endl;
        // 第一次调用静态函数GetResource()时才会加载该模型
        std::shared_ptr<Model> res = LazyResourceLoading::GetResource();
        std::cout << "After accessing resource." << std::endl;
    
        return 0;
    }
    
  • 单例模式变种:单例模式是一种常见的设计模式,该类只能有一个实例。在类的静态函数中,维护一个静态局部变量存储该类的一个对象,在获取该实例化对象时进行懒汉式初始化

    #include <iostream>
    #include <string>
    
    class Logger {
    public:
        static Logger& GetInstance() {
            static Logger instance; // 单例实例
            return instance;
        }
    
        void Log(const std::string& message) {
            std::cout << "Log: " << message << std::endl;
        }
    
    private:
        Logger() {
            std::cout << "Logger instance created." << std::endl;
        }
    
        ~Logger() {
            std::cout << "Logger instance destroyed." << std::endl;
        }
    };
    
    int main() {
        Logger& logger1 = Logger::GetInstance();
        Logger& logger2 = Logger::GetInstance();
    
        logger1.Log("Message 1");
        logger2.Log("Message 2");
    
        return 0;
    }
    

    在这里插入图片描述

    • 构造函数为私有,此类不能实例化对象
    • 想要获取它的对象,只能通过静态成员函数Logger::Getinstance()
    • 首次获取的时候这个对象才会被创建,生命周期等于程序的生命周期
    • 可以看到,我们获取了两次实例,但只有一次Logger类的实例构造函数和析构函数的调用
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

宗浩多捞

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

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

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

打赏作者

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

抵扣说明:

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

余额充值