如何禁用对象直接创建,仅允许通过new及智能指针实例化?
这个问题挺有意思的,确实可以实现强制堆分配、禁止栈上创建对象的需求,核心思路是利用C++的访问控制(私有/保护成员)限制栈对象的创建,同时给new和智能指针开"后门"。下面分两种常见方案来演示:
方案一:私有构造+静态工厂方法(最严谨,完全禁止直接
new) 这种方式彻底把构造函数藏起来,只允许通过类提供的静态方法创建对象,同时确保智能指针能正确销毁对象:
#include <memory> class Test { private: // 把构造函数设为私有,禁止直接创建(不管栈还是堆) Test() = default; // 析构函数也设为私有,确保只有授权的代码能销毁对象 ~Test() = default; // 让std::default_delete能访问私有析构,这样unique_ptr可以正常工作 friend class std::default_delete<Test>; public: // 静态工厂方法,返回unique_ptr static std::unique_ptr<Test> create_unique() { // 这里不能用std::make_unique,因为它需要访问私有构造函数 return std::unique_ptr<Test>(new Test()); } // 静态工厂方法,返回shared_ptr static std::shared_ptr<Test> create_shared() { // 用lambda作为删除器,因为lambda在类内部能访问私有析构 return std::shared_ptr<Test>(new Test(), [](Test* ptr) { delete ptr; }); } // 测试用的成员函数 void say_hello() const { printf("Hello from heap-allocated Test!\n"); } };
效果验证:
- 栈创建:
Test t;→ 编译错误(构造函数私有) - 直接
new:Test* ptr = new Test();→ 编译错误(构造函数私有) - 通过工厂创建
unique_ptr:auto ptr = Test::create_unique();→ 正常工作,销毁时自动调用析构 - 通过工厂创建
shared_ptr:auto ptr = Test::create_shared();→ 正常工作
方案二:公有构造+私有析构(允许直接
new,但禁止栈创建) 如果你想保留直接new的能力,但禁止栈对象,这种方案更贴合你的描述:
#include <memory> class Test { private: // 析构函数设为私有,栈对象无法自动调用析构,从而禁止栈创建 ~Test() = default; // 让std::default_delete访问析构,确保unique_ptr能正常销毁 friend class std::default_delete<Test>; public: // 构造函数公有,允许直接new Test() = default; // 提供静态方法手动销毁裸指针(因为直接delete会报错) static void destroy(Test* ptr) { delete ptr; } void say_hello() const { printf("Hello from heap-allocated Test!\n"); } };
效果验证:
- 栈创建:
Test t;→ 编译错误(析构私有,栈对象无法自动调用) - 直接
new:Test* ptr = new Test();→ 可以创建,但直接delete ptr;会报错(析构私有),必须用Test::destroy(ptr); unique_ptr:std::unique_ptr<Test> ptr(new Test());→ 正常工作,销毁时自动调用析构shared_ptr:如果直接std::shared_ptr<Test> ptr(new Test());会报错,需要用工厂方法或者自定义删除器,比如:auto ptr = std::shared_ptr<Test>(new Test(), [](Test* p) { Test::destroy(p); });
为什么这些方案能生效?
- 栈对象的销毁是自动的,编译器会在作用域结束时插入析构调用,如果析构函数私有,编译器无法生成这段代码,从而禁止栈创建。
- 堆对象的销毁是手动(
delete)或智能指针自动触发的,通过友元声明或类内部的lambda/静态方法,我们给这些销毁路径开了访问私有析构的权限。
注意事项(你也提到不会在生产用,这里提一下)
- 这种设计违反了C++的常规习惯,会让其他开发者困惑。
- 智能指针的使用会受限,比如
std::make_unique无法直接使用(因为需要访问构造函数)。 - 如果类有继承关系,私有析构会导致派生类无法正常工作,除非派生类也做相应的访问控制。
内容的提问来源于stack exchange,提问作者code
相关产品推荐
相关产品推荐

