zoukankan      html  css  js  c++  java
  • 移动构造函数,即是在用原对象指针对新对象指针进行赋值后,将原对象成员指针置为空指针

    C++11新特性学习笔记之移动构造函数
    指针成员和浅拷贝
    一般来说,如果一个类中有指针成员,则要小心拷贝成员函数的编写,因为如果不注意,则会造成程序的内存泄漏。如下所示的例子。

    #include <iostream>

    class HasPtrMem{
    public:
    HasPtrMem() : m_data(new int(0)){}
    ~HasPtrMem(){
    if (m_data != nullptr)
    {
    delete m_data;
    m_data = nullptr;
    }
    }
    int *m_data;
    };

    int main(){
    HasPtrMem a;
    HasPtrMem b(a);
    std::cout << a.m_data << std::endl;//0
    std::cout << b.m_data << std::endl;//0
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    我们申明了一个带有指针成员的HasPtrMem类,在main函数中声明了对象a,再用对象a去初始化b,由于类中没有显示声明拷贝构造函数,则按照C++语法会调用编译器隐式生成的拷贝构造函数。这样就会出现以下一个问题:

    a和b的m_data和都指向同一块堆内存,因此在main作用域结束后,a和b的析构函数依次被调用,比如b.m_data所指向的内存先被释放后,a.m_data就无法再对其进行操作,从而导致内存泄漏。

    上述拷贝构造方式就是浅拷贝(shallow copy):只对数据成员进行简单的赋值。

    深拷贝
    深拷贝(deep copy):针对存在指针数据成员的情况下,重新分配内存空间,不再是简单的指针赋值。如下所示。

    #include <iostream>
    class HasPtrMem{
    public:
    HasPtrMem() : m_data(new int(0)){}
    HasPtrMem(HasPtrMem& h) : m_data(new int(*h.m_data)){} //拷贝构造函数,从堆中分配内存,用h.m_data初始化
    ~HasPtrMem(){
    if (m_data != nullptr)
    {
    delete m_data;
    m_data = nullptr;
    }
    }
    int *m_data;
    };

    int main(){
    HasPtrMem a;
    HasPtrMem b(a);
    std::cout << *a.m_data << std::endl;//0
    std::cout << *b.m_data << std::endl;//0
    } //正常析构
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    运行结果:
    0
    0

    上述结果就不会报错,新的拷贝构造函数从堆中分配心内存,将分配来的内存的指针交给m._data,通过这样的方法,就能避免产生悬挂指针(dangling pointer)。

    移动构造函数
    如果指针所指向非常大的内存数据的话,则拷贝构造的代价就非常昂贵,会极大地影响性能。C++11提供一种简洁解决方法:移动构造函数,即是在用原对象指针对新对象指针进行赋值后,将原对象成员指针置为空指针,使得其无法指向内存数据,从而保证在析构的时候不会产生内存泄漏。这样既不用分配新内存,也不会产生内存泄漏,从而很好地解决了上述问题。如下所示。

    #include <iostream>

    class HasPtrMem{
    public:
    HasPtrMem() : m_data(new int(0)){
    std::cout << "Construct: " << ++n_cstr << std::endl;
    }
    HasPtrMem(const HasPtrMem &h) : m_data(new int(*h.m_data)){
    std::cout << "Copy construct: " << ++n_cptr << std::endl;
    }
    HasPtrMem(HasPtrMem&& h) :m_data(h.m_data){//移动构造函数
    h.m_data = nullptr; //将临时值的指针成员置空
    std::cout << "Move construct: " << ++n_mvtr << std::endl;
    }
    ~HasPtrMem(){
    if (m_data != nullptr)
    {
    delete m_data;
    m_data = nullptr;
    }
    std::cout << "Destruct: " << ++n_dstr << std::endl;
    }
    int *m_data;
    static int n_cstr;
    static int n_dstr;
    static int n_cptr;
    static int n_mvtr;
    };
    int HasPtrMem::n_cstr = 0;
    int HasPtrMem::n_cptr = 0;
    int HasPtrMem::n_mvtr = 0;
    int HasPtrMem::n_dstr = 0;

    HasPtrMem GetTemp(){
    HasPtrMem h;
    std::cout << "Resource from " << __FUNCTION__ << ":" << std::hex << h.m_data << std::endl;
    return h;
    }

    int main()
    {
    HasPtrMem a = GetTemp();
    std::cout << "Resource from " << __FUNCTION__ << ":" << std::hex << a.m_data << std::endl;
    system("pause");
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    运行结果:

    Construct: 1
    Resource from GetTemp:000000E2E9B55D00
    Move construct: 1
    Destruct: 1
    Resource from main:000000E2E9B55D00
    Destruct: 2
    1
    2
    3
    4
    5
    6
    GetTemp()函数返回了h的一个临时变量时,调用了移动构造函数,其中的h和main中的a的指针成员值是相同的,说明了h.m _data和a.m _data都指向了相同的堆内存地址.因此其指针在GetTemp函数返回时免于被析构的“命运”,成为main中a的变量。

    总结
    移动构造函数能解决占内存较大的对象的拷贝构造问题,对提高系统性能有很好的作用。
    ---------------------
    作者:Roy-bin
    来源:CSDN
    原文:https://blog.csdn.net/gxb0505/article/details/53572761
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    进位制 与成熟表示
    例题 3-6 环状序列
    -------------------开启我的手残之旅---------我就是喜欢写笔记-------咋滴啦?-----
    图的遍历---------开始开始-------o(∩_∩)o 哈哈
    -----------什么是图?------------
    并查集-----集合以及计算----
    ----堆----希望这是一个容易上手的工具--------
    kafka-docker----(how to setup http proxy in container??)
    FW: Dockerfile RUN, CMD & ENTRYPOINT
    telnet --- no route to host solution "iptables -F " in the target machine
  • 原文地址:https://www.cnblogs.com/findumars/p/11165842.html
Copyright © 2011-2022 走看看