C++类的特种函数生成机制详解
目录
- C++类的特种函数生成机制
- 规则
- 例子:A BUG
- 例子:std::mutex和std::thread
- 题外话:为什么std::mutex不可移动?
- 总结
C++类的特种函数生成机制
规则
参考Effective Morder C++上的说明:
- 默认构造函数:仅当类中不包含用户声明的构造函数时才生成。
- 析构函数:默认生成,当基类的析构函数为虚时,派生类的默认析构函数为虚函数。
- 拷贝构造函数:仅当类中不包含用户声明的拷贝构造函数时才生成。如果该类声明了移动操作,那么拷贝构造函数将被定义为删除的。
- 拷贝赋值运算符:仅当类中不包含用户声明的拷贝赋值运算符时才生成。如果该类声明了移动操作,那么拷贝赋值运算符将被定义为删除的。
- 移动构造函数和移动赋值运算符:仅当类中不包含用户声明的拷贝操作、移动操作和析构函数时才生成。
例子:A BUG
因为不熟悉析构函数的生成机制,导致了一个BUG。
首先,下面的代码没有问题,因为数据成员m_,所以Widget默认也是个只移型别;mm中也可以插入一个由只移型别构造的std::pair
class Widget {public:Widget() = default; //~Widget() = default; private:std::thread m_; // 只移型别}; unordered_mapmm; mm.insert({12, Widget()});
然后,我手贱加了一个默认的析构函数:
class Widget {public:Widget() = default; ~Widget() = default; private:std::thread m_; // 只移型别}; unordered_mapmm; mm.insert({12, Widget()}); // error!
报错信息极长,核心错误是:
error: no matching function for call to ‘std::unordered_map::insert( )'45 |unordered_map mm;
可以把std::pair的构造单独抽出来看到更清晰的报错信息:
// 代码如下:make_pair(12, Widget()); // 报错如下:In template: no matching constructor for initialization of '__pair_type' (aka 'pair')
“显然”,是因为Widget的移动构造函数被隐式删除了(它既不能拷贝也不能移动了),所以无法由Widget参数构造一个std::pair。
解决方案就是不要定义析构函数,或者显式定义一个移动构造函数:
class Widget {public:Widget() = default; Widget(Widget&&) = default; ~Widget() = default; private:std::thread m_; // 只移型别}; unordered_mapmm; mm.insert({12, Widget()});
例子:std::mutex和std::thread
在我做试验的时候,一开始错把std::mutex记成了只移型别,定义了一个这样的类:
class Widget {public:Widget() = default; private:std::mutex m_; }; unordered_mapmm; mm.insert({12, Widget()}); // error!
甚至在我没有添加析构函数的时候Widget就不能拷贝和移动了。
看看源码:
class mutex : private __mutex_base{public:/* ... */mutex() noexcept = default; ~mutex() = default; mutex(const mutex&) = delete; mutex& operator=(const mutex&) = delete; /* ... */}
显然,因为mutex自行定义了默认的析构函数而且把拷贝构造函数定义为删除的,那么它的移动构造函数也会被隐式删除,所以mutex既不能拷贝也不能移动。
和std::thread源码比较一下:
class thread{public:thread() noexcept = default; thread(const thread&) = delete; thread(thread&& __t) noexcept{ swap(__t); }~thread(){if (joinable())std::terminate(); }}
虽然std::thread定义了析构函数和删除的拷贝构造函数,但是它显式定义了移动构造函数,这使得它虽然不能拷贝但是可以移动。
题外话:为什么std::mutex不可移动? 大体来说就是std::mutex一般由多个线程调用,那么如果它的位置可以变化,那么怎么让所有线程都知道它的新位置在哪里呢?
详见stackoverflow: https://stackoverflow.com/questions/7557179/move-constructor-for-stdmutex
总结 【C++类的特种函数生成机制详解】本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!
推荐阅读
- 热闹中的孤独
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 一个人的旅行,三亚
- 布丽吉特,人生绝对的赢家
- 慢慢的美丽
- 尽力
- 一个小故事,我的思考。
- 家乡的那条小河
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量