3.8、default/delete(显式默认/删除成员函数)
什么是=default和=delete?
- • =default:告诉编译器“请帮我生成这个特殊成员函数的默认实现”,而不是自己写函数体。它可以恢复那些因为你写了其他构造函数而不自动生成的默认函数,或者显式声明默认行为,提升代码可读性和优化机会。
- • =delete:告诉编译器“这个函数我不想让你生成,也不允许调用”,用来禁止某些函数的使用,比如禁止拷贝构造或赋值操作,避免错误的对象复制和赋值。
传统写法 vs C++11写法对比
传统写法:禁止拷贝的笨办法
class NonCopyable {
private:
NonCopyable(const NonCopyable&); // 声明但不定义
NonCopyable& operator=(const NonCopyable&); // 声明但不定义
public:
NonCopyable() {}
};
这种写法靠私有化并不定义函数来阻止拷贝,但:
- • 代码晦涩,容易被误用。
- • 编译错误信息不明确。
- • 不能禁止移动操作。
C++11写法:显式默认和删除
class NonCopyable {
public:
NonCopyable() = default; // 显式默认构造函数
NonCopyable(const NonCopyable&) = delete; // 禁止拷贝构造
NonCopyable& operator=(const NonCopyable&) = delete; // 禁止拷贝赋值
};
这段代码:
- • 明确表达设计意图,代码更清晰。
- • 编译器会生成更友好的错误信息。
- • 禁止拷贝操作同时允许移动操作(除非也删除移动函数)。
设计哲学:为什么要有=default和=delete?
- • 明确控制特殊成员函数的生成:传统C++中,编译器自动生成特殊成员函数,但规则复杂且容易被用户定义的函数影响,导致默认函数缺失。
=default
让你显式恢复默认实现。 - • 防止错误使用函数:
=delete
提供了语言层面禁止函数调用的机制,比传统私有声明更安全、更直观。 - • 提升代码可读性和维护性:通过显式声明,代码意图一目了然,减少误用和隐藏bug。
- • 帮助编译器优化:显式默认的函数可能被编译器视为“平凡函数”,有助于优化。
最佳使用场景
- • 恢复默认构造函数:当你定义了其他构造函数但仍想保留默认构造时,使用
=default
。 - • 禁止拷贝和赋值:设计不可复制类时,使用
=delete
删除拷贝构造和赋值运算符。 - • 禁止特定函数重载:通过
=delete
禁止某些函数重载,避免隐式类型转换导致的错误调用。 - • 简化特殊成员函数定义:用
=default
代替空函数体,提升代码简洁度。
优缺点总结
优点 | 缺点 |
明确表达设计意图,代码更易读、易维护 | 需要程序员理解特殊成员函数生成规则,初学者有学习成本 |
编译器生成默认函数更高效,利于性能优化 | 误用=delete 可能导致接口限制过严,影响灵活性 |
=delete 防止错误调用,提升代码安全性 |
旧代码迁移时需要逐步添加,存在兼容性问题 |
支持禁止任意函数调用,不仅限于特殊成员函数 | 过度删除函数可能导致代码复用受限 |
常见误用及后果
- • 忘记写=default导致默认构造函数缺失:定义了带参数构造函数后,默认构造函数不再自动生成,若未显式
=default
,会导致编译错误。 - • 错误删除移动构造函数或移动赋值运算符:删除拷贝函数时未考虑移动函数,可能导致类不能移动,影响性能。
- • 滥用=delete限制接口:过度删除函数,限制了类的灵活使用和扩展。
- • 对非特殊成员函数使用=default:只有特殊成员函数可用
=default
,否则编译错误。
代码示例:显式默认与删除的综合运用
#include <iostream>
class Widget {
public:
Widget() = default; // 显式默认构造函数
Widget(int val) : value(val) {}
Widget(const Widget&) = delete; // 禁止拷贝构造
Widget& operator=(const Widget&) = delete; // 禁止拷贝赋值
Widget(Widget&&) = default; // 允许移动构造
Widget& operator=(Widget&&) = default; // 允许移动赋值
void print() const { std::cout << "Value: " << value << std::endl; }
private:
int value = 0;
};
int main() {
Widget w1(10);
Widget w2 = std::move(w1); // 移动构造,合法
// Widget w3(w1); // 编译错误,拷贝构造被删除
// w2 = w1; // 编译错误,拷贝赋值被删除
w2.print();
return 0;
}
总结:=default与=delete的独到价值
=default
和=delete
是C++11对类设计的精细化控制,它们不仅让程序员能够精确掌控特殊成员函数的生成和禁用,还提升了代码的安全性和可维护性。它们体现了现代C++追求明确意图、减少隐式行为、提升代码质量的设计哲学。
我认为,显式默认和删除函数的引入,是C++语言向更严谨、更现代、更易维护方向迈出的关键一步。合理运用它们,能让你的代码既简洁又健壮,避免传统C++中隐藏的陷阱和误用。
本文首发于【讳疾忌医-note】公众号,未经授权,不得转载。
(加入我的知识星球,免费获取账号,解锁所有文章。)
阅读剩余
版权声明:
作者:讳疾忌医-note
链接:https://www.1217zy.vip/archives/769
文章版权归作者所有,未经允许请勿转载。
THE END