You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++单例对象工厂实现:如何阻止用户直接创建对象实例?

这个问题我之前做项目的时候也碰到过,当时试了好几种方法,最后觉得两个方案最实用,分享给你:

方案一:CRTP(奇异递归模板模式)+ 友元传递

这种方式通过一个模板基类统一处理实例化权限,不用在每个业务类里单独加友元声明,代码结构很规整:

#include <iostream>

// 模板基类,负责传递友元权限和禁用拷贝移动
template <typename Derived>
class SingletonBase {
private:
    // 让基类成为派生类的友元,从而能访问派生类的私有构造函数
    friend Derived;
    SingletonBase() = default;
    ~SingletonBase() = default;

public:
    // 禁止拷贝和移动,避免单例被复制
    SingletonBase(const SingletonBase&) = delete;
    SingletonBase& operator=(const SingletonBase&) = delete;
    SingletonBase(SingletonBase&&) = delete;
    SingletonBase& operator=(SingletonBase&&) = delete;

    // 让工厂成为基类的友元,获得创建派生类实例的权限
    friend class SingletonFactory;
};

// 通用单例工厂类
class SingletonFactory {
public:
    // 获取单例实例,C++11及以后静态局部变量初始化是线程安全的
    template <typename T>
    static T& GetInstance() {
        static T instance;
        return instance;
    }
};

// 示例业务类:只需继承基类,构造函数设为私有即可
class UserService : public SingletonBase<UserService> {
private:
    UserService() = default; // 用户无法直接调用
public:
    void PrintInfo() {
        std::cout << "User Service Singleton Instance" << std::endl;
    }
};

int main() {
    // UserService service; // 编译错误:构造函数私有
    UserService& service = SingletonFactory::GetInstance<UserService>();
    service.PrintInfo();
    return 0;
}

优点:

  • 统一约束所有单例类的行为,避免重复代码
  • 基类可以统一禁用拷贝移动,不用每个类都写一遍
  • 业务类只需简单继承,侵入性低

缺点:

  • 依赖继承关系,对不希望使用继承的场景不太友好

方案二:密钥模式(Key Pattern)

这种方式更灵活,不需要继承,通过一个只有工厂能创建的"密钥"来控制构造权限:

#include <iostream>

// 密钥类:构造函数私有,只有工厂能创建
class SingletonKey {
private:
    SingletonKey() = default;
    friend class SingletonFactory;
};

// 通用单例工厂类
class SingletonFactory {
public:
    template <typename T>
    static T& GetInstance() {
        // 只有工厂能生成密钥,从而构造业务类实例
        static T instance(SingletonKey{});
        return instance;
    }
};

// 示例业务类:构造函数接受密钥参数,且设为私有
class OrderService {
private:
    // 只有持有密钥的对象(也就是工厂)才能调用这个构造函数
    explicit OrderService(SingletonKey) {}
public:
    void PrintInfo() {
        std::cout << "Order Service Singleton Instance" << std::endl;
    }

    // 禁止拷贝移动
    OrderService(const OrderService&) = delete;
    OrderService& operator=(const OrderService&) = delete;
};

int main() {
    // OrderService service; // 编译错误:无合适构造函数
    // OrderService service(SingletonKey{}); // 编译错误:密钥构造函数私有
    OrderService& service = SingletonFactory::GetInstance<OrderService>();
    service.PrintInfo();
    return 0;
}

优点:

  • 不需要继承,对现有类的修改极小
  • 逻辑直观,权限控制清晰

缺点:

  • 每个业务类都要写带密钥参数的私有构造函数,相比CRTP稍微繁琐一点

额外提醒

如果你的项目需要支持多线程,注意确保编译器是C++11或更高版本——因为从C11开始,静态局部变量的初始化是线程安全的,不需要额外加锁。如果是旧标准的C,可以考虑用双重检查锁,但要注意内存可见性问题(需要用volatile或std::atomic),还是推荐优先升级到C++11及以上版本。

内容的提问来源于stack exchange,提问作者Ava

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:08:00