温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

C++中类的特种函数生成机制原理分析

发布时间:2021-09-14 13:40:01 来源:亿速云 阅读:108 作者:小新 栏目:开发技术

这篇文章给大家分享的是有关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定义了析构函数和删除的拷贝构造函数,但是它显式定义了移动构造函数,这使得它虽然不能拷贝但是可以移动。

    感谢各位的阅读!关于“C++中类的特种函数生成机制原理分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    c++
    AI