• 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

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

  • 相关阅读:
    科技型中小企业认定条件和好处
    000-基于Sklearn的机器学习入门:工作环境搭建与配置
    基于ranger的kafka权限控制
    python常用知识梳理(必看篇)
    硬核性感!沉浸式体验 ZStack Cube 超融合的7大亮点功能
    Skywalking9.2.0自监控
    14 个你必须知道的JavaScript 函数
    Python爬虫实战:从API获取数据
    语法基础(函数)
    day4作业
  • 原文地址:https://blog.csdn.net/wodownload2/article/details/128171794