私有构造函数必须在private:区块中声明,仅private已阻止外部调用;但须同时将拷贝/移动构造及赋值运算符显式=delete,否则可通过复制绕过单例约束。

可以直接在 private: 区块里声明构造函数,不加任何特殊语法——它就是私有的。 没有额外关键字,也不需要 = delete 来“禁止”,private 本身已足够阻止外部调用。
私有构造函数的声明位置和基本写法
必须放在 private: 标签之后,且不能带 public 或 protected 修饰;否则就不是私有了。类内定义或类外定义都行,但访问权限由声明位置决定。
-
private:后面写的第一个构造函数(哪怕没参数)自动是私有的 - 如果同时存在多个构造函数(如带参、默认、移动),每个都得单独标为
private,否则漏掉的那个可能意外变成public - 类外定义时,无需重复写
private:,但必须确保声明已在private:区块中
示例:
class Logger {
private:
Logger() = default; // 私有默认构造
Logger(const std::string& name); // 私有带参构造
Logger(const Logger&) = delete; // 私有拷贝(显式禁用)
public:
static Logger& getInstance();
};
为什么必须同时禁用拷贝和移动
只设构造函数为私有,不代表对象无法被复制——一旦有人拿到实例(比如通过 getInstance()),就能尝试拷贝或赋值,而默认生成的拷贝构造/赋值操作符是 public 的,会绕过你的控制。
- 不删掉拷贝相关函数,别人写
Logger copy = Logger::getInstance();就能悄悄创建新对象 -
= delete是最直接的阻断方式,比手动定义为空更安全(编译期报错,而非运行时崩溃) - 即使类没有数据成员,也建议显式
= delete,避免未来扩展时遗漏
常见错误:误以为 = delete 等价于私有构造
= delete 和 private 是两回事:= delete 表示“这个函数存在,但禁止调用”;private 表示“这个函数只能被类内或友元访问”。两者常一起用,但作用不同。
- 仅写
Logger() = delete;而不加private:,外部仍可能通过友元或模板推导触发删除函数,报错信息是call to deleted function,不是访问权限错误 - 仅写
private: Logger();而不= delete拷贝函数,对象仍可被复制(只要没显式禁用) - 真正安全的单例写法是:私有化所有构造 + 显式
= delete所有拷贝/移动函数
静态工厂方法返回什么类型才合理
返回 static Logger& 最常用,但要注意生命周期绑定问题;返回 std::unique_ptr<logger></logger> 更灵活,但需配合私有析构(否则智能指针无法释放)。
- 返回引用(
Logger&):要求内部用静态局部变量(C++11 起线程安全),对象生存期全局,适合轻量单例 - 返回指针(
Logger*):需手动new/delete,容易泄漏,不推荐 - 返回
std::unique_ptr<logger></logger>:必须把析构函数也设为private,否则unique_ptr在销毁时无法访问析构函数 - 若类含资源(如文件句柄、网络连接),优先选值语义或智能指针,避免静态对象析构顺序问题
真正难处理的从来不是怎么写私有构造,而是怎么让使用者既无法绕过控制,又不会因静态生命周期或资源管理出错——这两点稍有疏忽,调试成本远高于定义本身。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











