MST
星途 面试题库

面试题:C++ 类内成员函数中静态成员的内存管理与生命周期问题

假设你有一个复杂的系统,其中有一个 `ResourceManager` 类,该类维护着一些静态成员,这些静态成员是指向动态分配资源的指针。类内有成员函数用于初始化和释放这些资源。在整个程序生命周期中,要确保这些资源的正确初始化和释放,尤其是要处理好静态成员在不同编译单元初始化顺序以及析构顺序带来的潜在问题。请详细描述你的设计方案,并给出关键代码示例。
27.2万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

设计方案

  1. 单例模式:使用单例模式来管理 ResourceManager,这样可以确保整个程序中只有一个实例,并且可以控制其初始化和析构的时机。
  2. 局部静态变量:在获取单例实例的函数中使用局部静态变量。这种方式可以保证局部静态变量在第一次调用该函数时被初始化,并且其初始化是线程安全的(C++11 及以后)。
  3. 资源释放:在单例的析构函数中释放所有动态分配的资源。为了处理静态成员在不同编译单元初始化顺序以及析构顺序带来的潜在问题,我们可以利用 atexit 函数注册一个清理函数,在程序正常退出时调用,以确保资源被正确释放。

关键代码示例

#include <iostream>

class Resource {
public:
    Resource() { std::cout << "Resource created" << std::endl; }
    ~Resource() { std::cout << "Resource destroyed" << std::endl; }
};

class ResourceManager {
private:
    static Resource* resource1;
    static Resource* resource2;

    // 私有构造函数,防止外部实例化
    ResourceManager() {
        initializeResources();
    }

    // 私有析构函数,防止外部销毁
    ~ResourceManager() {
        releaseResources();
    }

    // 禁止拷贝构造和赋值运算符
    ResourceManager(const ResourceManager&) = delete;
    ResourceManager& operator=(const ResourceManager&) = delete;

    void initializeResources() {
        resource1 = new Resource();
        resource2 = new Resource();
    }

    void releaseResources() {
        delete resource1;
        delete resource2;
    }

public:
    static ResourceManager& getInstance() {
        static ResourceManager instance;
        return instance;
    }

    // 提供获取资源的接口
    Resource* getResource1() { return resource1; }
    Resource* getResource2() { return resource2; }
};

// 静态成员变量的定义
Resource* ResourceManager::resource1 = nullptr;
Resource* ResourceManager::resource2 = nullptr;

// 用于在程序退出时清理资源的函数
void cleanup() {
    ResourceManager::getInstance(); // 确保单例已初始化
}

int main() {
    atexit(cleanup);

    ResourceManager& manager = ResourceManager::getInstance();
    Resource* res1 = manager.getResource1();
    Resource* res2 = manager.getResource2();

    // 使用资源

    return 0;
}

在上述代码中:

  1. ResourceManager 类使用单例模式,其构造函数和析构函数都是私有的,通过 getInstance 函数获取唯一实例。
  2. initializeResources 函数用于初始化静态资源指针,releaseResources 函数用于释放这些资源。
  3. atexit 函数注册了 cleanup 函数,在程序正常退出时调用,以确保资源被正确释放。