• C++:深拷贝和浅拷贝——拷贝构造、赋值构造必须自定义


    https://www.bilibili.com/video/BV1qT4y1X7cQ/?spm_id_from=333.337.search-card.all.click&vd_source=d33b44674c517c8b7928e8d3ac316b37

    1、赋值运算符重载

    浅拷贝的错误代码:

    class Distance
    {
    public:
    	int* dis = NULL;
    	Distance(int a)
    	{
    		dis = new int(a);
    	}
    
    	~Distance()
    	{
    		if (dis != NULL)
    		{
    			cout << "~distance" << endl;
    			delete dis;
    			dis = NULL;
    		}
    	}
    };
    
    
    int main()
    {
    	Distance p(23);
    	Distance p2(24);
    	p2 = p; //赋值构造
    	
    	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

    在这里插入图片描述
    C++默认提供给我们的赋值运算符函数,是浅拷贝。默认operator=是浅拷贝.
    默认operator=是浅拷贝.默认operator=是浅拷贝.默认operator=是浅拷贝.默认operator=是浅拷贝.

    由于是默认的系统提供的赋值函数;所以p2和p指向的是同一块内存;而析构的时候,p1先析构了堆内存;然后p2再去释放的时候,导致释放失败。
    同时p2原先指向的堆内存并未正确释放,导致内存泄露。

    定义自定义的赋值构造函数:

    class Distance
    {
    public:
    	int* dis = NULL;
    	Distance(int a)
    	{
    		dis = new int(a);
    	}
    
    	Distance& operator = (const Distance& other)
    	{
    		if (dis != NULL)
    		{
    			delete dis;
    			dis = NULL;
    		}
    
    		dis = new int(*other.dis);
    		return *this;
    	}
    
    	~Distance()
    	{
    		if (dis != NULL)
    		{
    			delete dis;
    			dis = NULL;
    		}
    	}
    };
    
    int main()
    {
    	Distance p(23);
    	Distance p2(24);
    	p2 = p;
    	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

    这样为啥不会出错了呢?因为p和p2分别指向自己的一块独立的内存,所以各自释放自己的内存不会失败;而如果是两个指针同时指向一块内存,那如果释放了多次,则会出错。

    当类对象作为参数传递的时候,调用的是拷贝构造函数,所以这里也会有拷贝:

    class Distance
    {
    public:
    	int* dis;
    	Distance(int a)
    	{
    		dis = new int(a);
    	}
    
    	~Distance()
    	{
    		if (dis != NULL)
    		{
    			cout << "~distance" << endl;
    			delete dis;
    			dis = NULL;
    		}
    	}
    };
    
    void func(Distance a)  //这里调用的是拷贝构造函数
    {
    	cout << *a.dis << endl;
    }
    
    int main()
    {
    	Distance p(23);
    	func(p);
    	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

    2、拷贝构造函数

    错误1:

    class Distance
    {
    public:
    	int* dis = NULL;
    	Distance(int a)
    	{
    		dis = new int(a);
    	}
    
    	~Distance()
    	{
    		if (dis != NULL)
    		{
    			delete dis;
    			dis = NULL;
    		}
    	}
    };
    
    int main()
    {
    	Distance p(23);
    	Distance p2(p); //拷贝构造, Distance p2 = p;这个也是调用拷贝构造,而不是赋值构造;
    
    	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

    由于C++默认提供的拷贝构造也是浅拷贝,导致俩指针指向同一个内存,所以释放多次导致错误。
    Distance p2§; //拷贝构造, Distance p2 = p;这个也是调用拷贝构造,而不是赋值构造;

    正确做法:

    class Distance
    {
    public:
    	int* dis = NULL;
    	Distance(int a)
    	{
    		dis = new int(a);
    	}
    
    	Distance(const Distance& other)
    	{
    		cout << "copy constructor" << endl;
    		if (dis != NULL)
    		{
    			delete dis;
    		}
    		dis = NULL;
    		this->dis = new int(*other.dis);
    	}
    
    	~Distance()
    	{
    		if (dis != NULL)
    		{
    			delete dis;
    			dis = NULL;
    		}
    	}
    };
    
    int main()
    {
    	Distance p(23);
    	Distance p2 = p;
    
    	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

    总结:带有指针的成员,需要自定义拷贝构造、赋值构造函数,使得每个对象都有自己独立的内存空间,防止内存释放报错。

  • 相关阅读:
    不见他过: 职场经验
    Redis事务、pub/sub、PipeLine-管道、benchmark性能测试详解
    【华为机试真题 JAVA】字符串简单数据解压缩-100
    轻量封装WebGPU渲染系统示例<16>- 使用GPU Compute计算(源码)
    shell知识点复习
    ASO优化之手游该如何获得巨量新增(上)
    [LCT 刷题][树链信息维护] P2486 [SDOI2011]染色
    Java安全之freemaker模版注入
    【深度学习】【pytorch】对卷积层置零卷积核进行真实剪枝
    基于SNN脉冲神经网络的Hebbian学习训练过程matlab仿真
  • 原文地址:https://blog.csdn.net/wodownload2/article/details/128171794