真正禁止实例化需两类方法:一是用纯虚函数使类抽象(如virtual void f() = 0),二是显式delete所有构造/分配函数;抽象类适合需继承的接口,工具类优先用namespace。

class 不能被实例化,不是靠“加个注释”或“不写构造函数”实现的——C++ 编译器默认会合成默认构造函数,只要没显式阻止,就可能被调用。真正有效的办法只有两类:让类变成抽象类,或彻底封死所有构造路径。
用纯虚函数制造抽象类
最直接、语义最清晰的方式:在类中声明至少一个 virtual 函数并赋值为 = 0。
- 哪怕只是
virtual void dummy() = 0;,也足以让编译器拒绝MyClass obj;或new MyClass - 错误信息明确:
error: cannot declare variable of abstract type 'MyClass' - 适合需要被继承、强制子类实现接口的场景;但注意:如果类本意是工具类(无状态、全静态),走这条路反而引入了不必要的虚表开销和继承语义
- 析构函数也可以设为纯虚:
virtual ~MyClass() = 0;,但必须在类外提供定义(哪怕空实现),否则链接失败
删掉所有构造函数(C++11 起推荐)
对工具类更干净的做法:显式 = delete 所有可被调用的构造方式。
- 必须删掉全部:默认构造、拷贝构造、移动构造、以及
operator new和operator delete(否则new MyClass仍可能成功) - 示例:
MyClass() = delete;、MyClass(const MyClass&) = delete;、void* operator new(std::size_t) = delete; - 只留
static成员函数?那干脆考虑用namespace替代——没有类、没有构造问题、更轻量 - 友元函数或模板实例化可能绕过限制:如果类声明了
friend class Helper;,Helper 仍能调用私有/删除的构造函数
私有构造 + 不提供访问入口 = 实际不可实例化
老派写法,但容易误用:仅把构造函数设为 private 并不保险。
-
private:构造函数 + 没有public static工厂方法 → 外部确实无法构造,但内部静态成员或友元仍可创建 - 常见误写:
private: MyClass() = default;—— 这只是禁止外部调用,但若类内有static MyClass instance;,照样能实例化 - 如果真要单例,必须连拷贝/移动都
= delete,否则MyClass x = getInstance();可能触发拷贝构造(即使你没写) - 继承时尤其危险:
protected构造函数允许派生类调用,但客户代码仍可能通过继承绕过限制
最容易被忽略的破口:友元与模板
你以为删光了构造函数就万事大吉?不一定。
- 任何声明为
friend的函数或类,都能无视访问控制,直接调用private或deleted构造函数 - 模板实例化时,如果模板代码里写了
T(),而T是你的禁实例化类,编译器会在实例化点报错——但错误位置可能远离你预期的地方,排查费劲 - 如果类里有
constexpr构造函数且被= delete,某些 constexpr 上下文(如数组大小推导)可能触发更隐蔽的编译失败
真正想“禁止实例化”,得同时堵住语言层面所有合法入口:构造、分配、复制、移动、友元、模板推导。多数情况下,抽象类或命名空间比“拼命删函数”更可靠。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











