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
相关产品推荐
相关产品推荐

