C++类的特种函数生成机制详解

 更新时间:2021年09月14日 11:54:18   作者:saddlesad  
这篇文章主要给大家介绍了关于C++类特种函数的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

C++类的特种函数生成机制

规则

参考Effective Morder C++上的说明:

  • 默认构造函数:仅当类中不包含用户声明的构造函数时才生成。
  • 析构函数:默认生成,当基类的析构函数为虚时,派生类的默认析构函数为虚函数。
  • 拷贝构造函数:仅当类中不包含用户声明的拷贝构造函数时才生成。如果该类声明了移动操作,那么拷贝构造函数将被定义为删除的。
  • 拷贝赋值运算符:仅当类中不包含用户声明的拷贝赋值运算符时才生成。如果该类声明了移动操作,那么拷贝赋值运算符将被定义为删除的。
  • 移动构造函数和移动赋值运算符:仅当类中不包含用户声明的拷贝操作、移动操作和析构函数时才生成。

例子:A BUG

因为不熟悉析构函数的生成机制,导致了一个BUG。

首先,下面的代码没有问题,因为数据成员m_,所以Widget默认也是个只移型别;mm中也可以插入一个由只移型别构造的std::pair<int, Widget>,因为pair默认支持右值参数构造(可以由只移的Widget构造)和自身的移动构造函数(可以移动构造到unordered_map中):

class Widget {
public:
    Widget() = default;
//    ~Widget() = default;
private:
    std::thread m_; // 只移型别
};
unordered_map<int, Widget> mm;
mm.insert({12, Widget()});

然后,我手贱加了一个默认的析构函数:

class Widget {
public:
    Widget() = default;
    ~Widget() = default;
private:
    std::thread m_; // 只移型别
};
unordered_map<int, Widget> mm;
mm.insert({12, Widget()}); // error!

报错信息极长,核心错误是:

error: no matching function for call to ‘std::unordered_map<int, Widget>::insert(<brace-enclosed initializer list>)'
   45 |     unordered_map<int, Widget> mm;

可以把std::pair的构造单独抽出来看到更清晰的报错信息:

// 代码如下:
make_pair(12, Widget());
// 报错如下:
In template: no matching constructor for initialization of '__pair_type' (aka 'pair<int, Widget>')

“显然”,是因为Widget的移动构造函数被隐式删除了(它既不能拷贝也不能移动了),所以无法由Widget参数构造一个std::pair。

解决方案就是不要定义析构函数,或者显式定义一个移动构造函数:

class Widget {
public:
    Widget() = default;
    Widget(Widget&&) = default;
    ~Widget() = default;
private:
    std::thread m_; // 只移型别
};
unordered_map<int, Widget> mm;
mm.insert({12, Widget()});

例子:std::mutex和std::thread

在我做试验的时候,一开始错把std::mutex记成了只移型别

定义了一个这样的类:

class Widget {
public:
    Widget() = default;
private:
    std::mutex m_;
};
unordered_map<int, Widget> mm;
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++11/14 线程调用类对象和线程传参的方法

    C++11/14 线程调用类对象和线程传参的方法

    这篇文章主要介绍了C++11/14 线程调用类对象和线程传参的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-01-01
  • C++基于回溯法解决八皇后问题示例

    C++基于回溯法解决八皇后问题示例

    这篇文章主要介绍了C++基于回溯法解决八皇后问题,简单描述了八皇后问题,以及回溯法的原理与解决八皇后问题的相关操作技巧,需要的朋友可以参考下
    2017-11-11
  • C语言技巧提升之回调函数的掌握

    C语言技巧提升之回调函数的掌握

    这篇文章主要为大家详细介绍一下C语言中回调函数的用法教程,文中的示例代码讲解详细,对我们学习C语言有一定帮助,需要的可以参考一下
    2022-12-12
  • C语言实现双向链表

    C语言实现双向链表

    本文给大家分享的是一段使用C语言实现双向链表的代码,完全是根据自己的理解和认识来编写的,希望大家能够喜欢,文章的最后附上了一个网友写的对于双向链表删除节点、插入节点、双向输出等操作的代码,也非常不错,推荐给大家
    2015-03-03
  • C语言解决字符串中插入和删除某段字符串问题

    C语言解决字符串中插入和删除某段字符串问题

    这篇文章主要介绍了C语言解决字符串中插入和删除某段字符串问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • C++中构造函数重载

    C++中构造函数重载

    这篇文章主要介绍了C++中构造函数重载的相关资料,十分的详细,需要的朋友可以参考下
    2015-06-06
  • C++ 11实现检查是否存在特定的成员函数

    C++ 11实现检查是否存在特定的成员函数

    C++11/14相比以往的C++98/03在很多方面做了简化和增强,尤其是在泛型编程方面,让C++的泛型编程的威力变得更加强大,下面这篇文章主要介绍了利用C++ 11实现检查是否存在特定成员函数的相关资料,需要的朋友可以参考下。
    2017-02-02
  • c++ #include是怎么样工作的?

    c++ #include是怎么样工作的?

    大多数园友可能对“#include”比较熟悉,因为我们写C/C++程序的时候都会写的字符串之一,但是它是具体怎么工作的?或者它的原理是什么呢?
    2013-01-01
  • c++ 数字类型和字符串类型互转详解

    c++ 数字类型和字符串类型互转详解

    今天小编就为大家分享一篇讲解c++ 数字类型和字符串类型互转的文章,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-09-09
  • C++ Qt QColorDialog使用方法

    C++ Qt QColorDialog使用方法

    本文主要介绍了C++ Qt QColorDialog使用方法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01

最新评论