面试题:C++ 中如何实现一个单例模式?

在 C++ 中,单例模式(Singleton Pattern)是一种设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。以下是几种实现单例模式的常见方法:


1. 懒汉式(Lazy Initialization)

懒汉式单例模式在第一次调用时创建实例。

优点:

  • 延迟初始化:只有在需要时才创建实例,节省资源。

缺点:

  • 线程不安全:多线程环境下可能会创建多个实例。

示例:

class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton instance;  // 局部静态变量,线程安全(C++11 及以上)
        return instance;
    }

    void doSomething() {
        std::cout << "Doing something..." << std::endl;
    }

private:
    Singleton() {}  // 私有构造函数
    Singleton(const Singleton&) = delete;  // 删除拷贝构造函数
    Singleton& operator=(const Singleton&) = delete;  // 删除赋值运算符
};

2. 饿汉式(Eager Initialization)

饿汉式单例模式在程序启动时创建实例。

优点:

  • 线程安全:实例在程序启动时创建,避免了多线程问题。

缺点:

  • 资源浪费:即使不需要实例,也会在程序启动时创建。

示例:

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

    void doSomething() {
        std::cout << "Doing something..." << std::endl;
    }

private:
    Singleton() {}  // 私有构造函数
    Singleton(const Singleton&) = delete;  // 删除拷贝构造函数
    Singleton& operator=(const Singleton&) = delete;  // 删除赋值运算符

    static Singleton instance;  // 静态成员变量
};

Singleton Singleton::instance;  // 在程序启动时初始化

3. 双重检查锁定(Double-Checked Locking)

双重检查锁定是一种线程安全的懒汉式单例模式。

优点:

  • 线程安全:通过双重检查锁定机制,确保多线程环境下只创建一个实例。
  • 延迟初始化:只有在需要时才创建实例。

缺点:

  • 实现复杂:需要手动管理锁和内存屏障。

示例:

#include <mutex>

class Singleton {
public:
    static Singleton& getInstance() {
        if (instance == nullptr) {  // 第一次检查
            std::lock_guard<std::mutex> lock(mutex);  // 加锁
            if (instance == nullptr) {  // 第二次检查
                instance = new Singleton();
            }
        }
        return *instance;
    }

    void doSomething() {
        std::cout << "Doing something..." << std::endl;
    }

private:
    Singleton() {}  // 私有构造函数
    Singleton(const Singleton&) = delete;  // 删除拷贝构造函数
    Singleton& operator=(const Singleton&) = delete;  // 删除赋值运算符

    static Singleton* instance;  // 静态成员变量
    static std::mutex mutex;  // 互斥锁
};

Singleton* Singleton::instance = nullptr;  // 初始化为空
std::mutex Singleton::mutex;  // 初始化互斥锁

4. Meyers’ Singleton(局部静态变量)

Meyers’ Singleton 是一种线程安全的懒汉式单例模式,利用局部静态变量的特性。

优点:

  • 线程安全:C++11 及以上标准保证局部静态变量的初始化是线程安全的。
  • 延迟初始化:只有在需要时才创建实例。
  • 实现简单:代码简洁,易于理解。

缺点:

  • 依赖 C++11 及以上标准:需要编译器支持 C++11 或更高版本。

示例:

class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton instance;  // 局部静态变量,线程安全(C++11 及以上)
        return instance;
    }

    void doSomething() {
        std::cout << "Doing something..." << std::endl;
    }

private:
    Singleton() {}  // 私有构造函数
    Singleton(const Singleton&) = delete;  // 删除拷贝构造函数
    Singleton& operator=(const Singleton&) = delete;  // 删除赋值运算符
};

5. 使用 std::call_once

std::call_once 是 C++11 提供的一种线程安全的初始化机制。

优点:

  • 线程安全:确保多线程环境下只初始化一次。
  • 延迟初始化:只有在需要时才创建实例。

缺点:

  • 依赖 C++11 及以上标准:需要编译器支持 C++11 或更高版本。

示例:

#include <mutex>

class Singleton {
public:
    static Singleton& getInstance() {
        std::call_once(initFlag, []() {
            instance = new Singleton();
        });
        return *instance;
    }

    void doSomething() {
        std::cout << "Doing something..." << std::endl;
    }

private:
    Singleton() {}  // 私有构造函数
    Singleton(const Singleton&) = delete;  // 删除拷贝构造函数
    Singleton& operator=(const Singleton&) = delete;  // 删除赋值运算符

    static Singleton* instance;  // 静态成员变量
    static std::once_flag initFlag;  // 初始化标志
};

Singleton* Singleton::instance = nullptr;  // 初始化为空
std::once_flag Singleton::initFlag;  // 初始化标志

6. 使用智能指针

使用 std::unique_ptrstd::shared_ptr 管理单例实例,避免手动管理内存。

优点:

  • 自动管理内存:避免内存泄漏。
  • 线程安全:结合 std::call_once 或局部静态变量实现线程安全。

示例:

#include <memory>
#include <mutex>

class Singleton {
public:
    static std::shared_ptr<Singleton> getInstance() {
        std::call_once(initFlag, []() {
            instance = std::shared_ptr<Singleton>(new Singleton());
        });
        return instance;
    }

    void doSomething() {
        std::cout << "Doing something..." << std::endl;
    }

private:
    Singleton() {}  // 私有构造函数
    Singleton(const Singleton&) = delete;  // 删除拷贝构造函数
    Singleton& operator=(const Singleton&) = delete;  // 删除赋值运算符

    static std::shared_ptr<Singleton> instance;  // 静态成员变量
    static std::once_flag initFlag;  // 初始化标志
};

std::shared_ptr<Singleton> Singleton::instance = nullptr;  // 初始化为空
std::once_flag Singleton::initFlag;  // 初始化标志

总结

方法线程安全延迟初始化实现复杂度依赖标准
懒汉式不安全简单
饿汉式安全简单
双重检查锁定安全复杂
Meyers’ Singleton安全简单C++11 及以上
std::call_once安全中等C++11 及以上
智能指针安全中等C++11 及以上

选择建议

  • 简单场景:推荐使用 Meyers’ Singleton(局部静态变量)。
  • 需要手动管理内存:推荐使用 双重检查锁定std::call_once
  • 需要自动管理内存:推荐使用 智能指针
THE END
点赞12 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容