c++之移动构造函数或者移动赋值运算符的作用详解

 更新时间:2025年03月04日 09:47:09   作者:_DCG_  
文章介绍了移动构造函数和移动赋值运算符的核心作用,它们通过资源管理权转移的方式实现对象的构造,减少内存拷贝的开销,移动构造函数的参数一定是一个右值引用

原理介绍

前面的文章中有的涉及到了移动构造函数或者移动赋值运算符,对于它们的形式有了一定的了解,但是对他们的核心作用以及为什么要引入这两个东东,很多朋友还是一知半解。本文就是来解决这个问题的。

要理解这个问题绕不开右值引用,c++11之前的 版本中只有拷贝构造函数,而拷贝构造函数是一般会深拷贝,即会创建两个完全一样的对象,包括指针指向的区域都会进行重新申请内存和拷贝。这种情况下如果对象是一个大数组或占用资源多的对象,就会产生很大的内存拷贝开销。那么如何解决这个问题呢?

c+11版本引入了右值引用、移动语义来解决这个问题。这里先说结论:通过移动构造函数或移动赋值运算符产生的对象与元对象会产生一个资源的转移。举个形象的例子:小明手里有个篮球,小王手里没有篮球,以前的拷贝构造函数或者拷贝赋值运算符重载的结果是小王会按照小明手里的篮球重新买一个;而移动构造函数和移动赋值运算符重载实现则更像是小王把小明手里的篮球放到自己手里,而小明手里没有了篮球。

从上面的例子可以看出移动构造函数或移动赋值运算符的作用就是通过资源管理权转移的方式实现对象的构造,使得可以减少内存拷贝的开销。

移动构造函数的的参数一定是一个右值引用。

下面是一个给出一个移动构造函数的典型的例子:

class MyClass
{
    std::string str;  //一个类成员
    int* ptr;  //一个指针成员

    public: 
        A(){}
        A(MyClass && a)::str(std::move(a.str))
        {
            
        }

        A operator=(MyClass&& a)  //移动赋值运算符重载
        {
            if(this != &a)
            {
                str = std:move(a.str); //这里因为str::string类已经实现了移动构造赋值运算符,std::string作为一个类已经支持了这个,所以可以这样做,如果是
                ptr = a.ptr;  //需要自己去切换指针所指向的区域
                a.ptr = nullptr;  
            }
        }

}

注意事项

  1. 一个类的移动构造函数或移动赋值运算符的实现是由类自己实现的
  2. 如果要使用移动构造函数或者移动赋值运算符,那么用户必须显式的定义移动构造函数和移动赋值运算符的重载
  3. 一些c++标准库中提供的类很多都已经实现了移动构造函数和移动赋值运算符的重载,例如std::string、stl容器类、智能指针(std::shared_ptr<T>、std::unique_ptr<T>)等
  4. 用户自己定义类在有需要的情况下需要在自定义的类中实现移动构造函数和移动赋值运算符重载。
  5. 移动构造函数或移动赋值运算符重载实现基于右值引用,一定是以右值引用作为参数的

思考

移动构造函数本质是资源管理权的转移,那如果跟std::shared_ptr<T>会发生什么?

  • 请看下面的代码:
class MyClass
{
    public:
        A(){
            std::cout<<"A的构造函数"<<std::endl;
        }
        ~A(){
            std::cout<<"A的析构函数"<<std::endl;
        }   
}

class Container
{
    private:
        std::shared_ptr<MyClass> ptr;
    public:
        Container(Container&& container):ptr(std::move(container.ptr))
        {
            std::cout<<"移动构造函数"<<std::endl;
        }
        Container operator=(Container&& container)
        {
            if(this != &container)  //这里不能少
            {        
                ptr = std::move(container.ptr); //这里也可以,因为std::shared_ptr内部实习拿了移动赋值运算符
            }    
        }
}

shared_ptr 的移动操作:

  • 转移资源所有权
  • 源指针置空
  • 保持引用计数不变
  • 是线程安全和异常安全的

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • C++实现Date类各种运算符重载的示例代码

    C++实现Date类各种运算符重载的示例代码

    这篇文章主要为大家详细介绍了C++实现Date类各种运算符重载的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-02-02
  • C语言之选择分支语句详解

    C语言之选择分支语句详解

    大家好,本篇文章主要讲的是C语言之选择分支语句详解,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • libxml教程(图文详解)

    libxml教程(图文详解)

    本篇文章是对libxm进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • ubuntu修改gcc版本的操作方法

    ubuntu修改gcc版本的操作方法

    今天小编就为大家分享一篇ubuntu修改gcc版本的操作方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12
  • c++实现图像像素计算的示例详解

    c++实现图像像素计算的示例详解

    我们知道每张图像都能够用矩阵来表示,矩阵中每个元素的值表示了图像中每个像素值,像素值的大小就对应着图像的亮暗,本文主要来和大家介绍一下C++进行图像像素计算的相关知识,感兴趣的可以了解下
    2023-12-12
  • C++使用TinyXML解析XML

    C++使用TinyXML解析XML

    本文详细讲解了C++使用TinyXML解析XML的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • c++快速排序详解

    c++快速排序详解

    快速排序总体思想:先找到一个枢轴,让他作为分水岭,通过一趟排序将待排序的记录分割成两部分,前面一部分都比枢轴小,后面一部分枢轴大,然后又分别对这两部分记录继续进行递归的排序,达到整个序列有序的目的
    2017-05-05
  • QML与C++交互的实现步骤

    QML与C++交互的实现步骤

    本文主要介绍了QML与C++交互的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • 关于C++中构造函数初始化成员列表的总结

    关于C++中构造函数初始化成员列表的总结

    下面小编就为大家带来一篇关于C++中构造函数初始化成员列表的总结。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • C++中的strcmp函数

    C++中的strcmp函数

    strcmp函数是C++标准库中用于字符串比较的重要函数,在C++中,字符串比较是一项常见的操作,用于判断两个字符串是否相等或者大小关系,本文介绍C++中的strcmp函数,感兴趣的朋友一起看看吧
    2024-03-03

最新评论