C++ 中的 delete 与 default 关键字详解
在 C++ 中,delete
和 default
是两个用于控制类特殊成员函数行为的关键字,它们在现代 C++ 编程中扮演着重要角色。下面我将详细解释这两个关键字的用法、区别以及实际应用场景。
一、delete
关键字
1. 基本作用
delete
用于显式禁止某些函数的使用(包括任何形式的调用)
2. 主要应用场景
禁止拷贝操作
class NonCopyable {
public:NonCopyable() = default;// 禁止拷贝构造函数NonCopyable(const NonCopyable&) = delete;// 禁止拷贝赋值运算符NonCopyable& operator=(const NonCopyable&) = delete;
};int main() {NonCopyable a;// NonCopyable b = a; // 错误:拷贝构造函数被删除// a = NonCopyable(); // 错误:拷贝赋值运算符被删除
}
禁止特定参数类型的函数
class Logger {
public:void log(int value) { /* ... */ }void log(double) = delete; // 禁止使用 double 参数调用
};int main() {Logger log;log.log(42); // OK// log.log(3.14); // 错误:log(double) 被删除
}
禁止不希望的隐式转换
class MyInt {int value;
public:MyInt(int v) : value(v) {} // 允许 int 构造MyInt(char) = delete; // 禁止 char 构造
};int main() {MyInt a(10); // OK// MyInt b('x'); // 错误:构造函数被删除
}
3. 重要特性
比 C++03 的 private 未实现方法更安全(编译时错误 vs 链接时错误)
可应用于任何函数(成员函数、非成员函数、运算符)
必须在函数第一次声明时删除
支持模板特化删除
二、default
关键字
1. 基本作用
default
用于显式请求编译器生成函数的默认实现
2. 主要应用场景
恢复默认构造函数
class Widget {
public:// 自定义构造函数会抑制默认构造函数Widget(int value) { /* ... */ }// 显式恢复默认构造函数Widget() = default;
};int main() {Widget w1(10); // 使用自定义构造函数Widget w2; // 使用默认构造函数
}
显式声明特殊成员函数
3. 重要特性
只能用于有默认实现的特殊成员函数:
默认构造函数
析构函数
拷贝构造函数
拷贝赋值运算符
移动构造函数 (C++11)
移动赋值运算符 (C++11)
生成的内联性与隐式生成相同
可在类内定义(隐式内联)或类外定义
三、对比总结表
特性 | delete | default |
---|---|---|
目的 | 禁止函数使用 | 显式生成默认实现 |
适用函数 | 任何函数 | 特殊成员函数 |
主要用途 | 1. 禁止拷贝/移动 2. 禁止特定重载 3. 禁止隐式转换 | 1. 恢复默认构造函数 2. 显式声明特殊成员 3. 保持平凡类型特性 |
编译检查 | 编译时报错 | 编译时生成代码 |
对类的影响 | 使类不可拷贝/不可调用 | 保持类的平凡性(trivial) |
C++版本 | C++11 引入 | C++11 引入 |
四、联合使用示例
class Advanced {
public:// 显式生成默认构造函数Advanced() = default;// 禁止拷贝Advanced(const Advanced&) = delete;Advanced& operator=(const Advanced&) = delete;// 允许移动Advanced(Advanced&&) = default;Advanced& operator=(Advanced&&) = default;// 禁止特定参数类型的调用void process(int) {}void process(double) = delete;
};int main() {Advanced a;Advanced b(std::move(a)); // OK:使用移动构造// Advanced c(a); // 错误:拷贝构造被删除a.process(10); // OK// a.process(3.14); // 错误:double版本被删除
}
五、最佳实践指南
1. delete
使用建议
优先于私有声明:使用
=delete
替代传统的私有未实现方法禁用拷贝时同时禁用赋值:保持一致性
用于接口净化:禁止不希望的参数类型转换
配合类型系统:创建更安全的API
// 防止在堆上分配对象
class StackOnly {
public:void* operator new(size_t) = delete;void operator delete(void*) = delete;
};
2. default
使用建议
明确设计意图:显式声明而非依赖隐式生成
恢复默认构造函数:当存在其他构造函数时
保持平凡性:当需要平凡可复制类型时
移动操作:需要时显式声明(否则可能抑制生成)
// 保持平凡可复制特性
struct TrivialType {int x;double y;TrivialType() = default;TrivialType(const TrivialType&) = default;TrivialType& operator=(const TrivialType&) = default;~TrivialType() = default;
};
3. 现代 C++ 类设计模式
五法则模式 (Rule of Five)
class ResourceHandler {int* resource;
public:// 构造函数explicit ResourceHandler(size_t size) : resource(new int[size]) {}// 1. 析构函数~ResourceHandler() { delete[] resource; }// 2. 拷贝构造函数ResourceHandler(const ResourceHandler& other) : resource(new int[/*size*/]) {std::copy(/*...*/);}// 3. 拷贝赋值运算符ResourceHandler& operator=(const ResourceHandler& rhs) {if (this != &rhs) {// 拷贝实现}return *this;}// 4. 移动构造函数ResourceHandler(ResourceHandler&& other) noexcept : resource(std::exchange(other.resource, nullptr)) {}// 5. 移动赋值运算符ResourceHandler& operator=(ResourceHandler&& rhs) noexcept {if (this != &rhs) {delete[] resource;resource = std::exchange(rhs.resource, nullptr);}return *this;}
};
零法则模式 (Rule of Zero)
// 使用智能指针和标准库组件,无需自定义特殊成员
class RuleOfZero {std::unique_ptr<int[]> data; // 自动处理资源std::vector<double> items; // 自动管理内存
public:RuleOfZero(size_t size) : data(std::make_unique<int[]>(size)) {}// 不需要声明任何特殊成员函数// 编译器会自动生成正确的拷贝/移动操作或删除它们
};
六、注意事项与常见错误
1. default
的限制
不能用于普通成员函数
不能改变函数的默认行为
类外定义时不能添加
inline
等修饰符
class Example {
public:// 错误:不能 default 普通函数void normalFunction() = default;
};
2. delete
的特殊用法
可删除 new 运算符防止堆分配
可删除析构函数防止栈分配(需配合 new)
// 只能在堆上创建的对象 class HeapOnly { public:void destroy() { delete this; } private:~HeapOnly() = default; // 私有析构 };// 只能在栈上创建的对象 class StackOnly { public:void* operator new(size_t) = delete; };
3. 继承关系中的影响
基类的删除函数会被派生类继承
虚函数可以删除,但需注意多态调用风险
class Base { public:virtual void func() = delete; };class Derived : public Base { public:void func() override {} // 错误:尝试覆盖已删除函数 };
七、C++20/23 增强
1. 显式对象参数 (C++23)
struct S {void f(this S&); // 显式对象参数void f(this S&&) = delete; // 禁止右值调用 };
2.
=delete
支持自定义消息 (提案)// 未来可能支持 void deprecated() = delete("Use new_function instead");
总结
delete
和default
是现代 C++ 中管理类特殊成员函数的核心机制:delete
提供更强的接口约束能力default
提供更清晰的默认行为控制关键实践原则:
优先使用 Rule of Zero,让编译器自动处理资源
需要特殊处理时使用 Rule of Five
使用
delete
明确禁止不希望的接口使用
default
明确依赖编译器默认行为在类定义中显式声明特殊成员函数的意图