C++-Record21—浅拷贝及深拷贝问题

目录

浅拷贝问题抛出

浅拷贝问题的分析

浅拷贝问题的解决方案

默认等号操作符执行赋值操作时也是浅拷贝

总体代码


浅拷贝问题抛出

先看一段代码,定义一个Name类,类里面,手工定义的一个构造函数贺析构函数,没有定义拷贝函数:

class  Name
{
public:
	Name(const char *myp)
	{
		m_len = strlen(myp);
		m_p =(char *) malloc(m_len + 1); //
		strcpy(m_p, myp);
	}

	//Name obj2 = obj1;

	~Name()
	{
		if (m_p != NULL)
		{
			free(m_p);
			m_p = NULL;
			m_len = 0;
		}
	}
protected:
private:
	char *m_p ;
	int m_len; 
};

 开始进行调用,会发现中间这步"Name obj2 = obj1;"是需要调用拷贝构造函数的,但类中并没有手动定义,所以,调用的是编译器自动匹配的拷贝构造函数。

void objplaymain()
{
	Name obj1("abcdefg");
	Name obj2 = obj1;  //C++编译器提供的 默认的copy构造函数  浅拷贝
}

void main91()
{
	objplaymain();
	cout<<"hello..."<<endl;
	system("pause");
	return ;
}

但是,这个代码是无法运行的,无法运行的原因就是,因为编译器自动设置的拷贝构造函数出现了问题。

补充:

在上面的代码中,弄不清楚字符串是怎么拷贝的,回翻看:

"C语言指针"专栏的"Record12—字符串1级指针的内存模型",这里有关于字符串的详细叙述。

https://blog.csdn.net/qq_17846375/article/details/103679709

定义函数:char *strcpy(char *dest, const char *src);

函数说明:strcpy()会将参数src 字符串拷贝至参数dest 所指的地址。

返回值:返回参数dest 的字符串起始地址。

浅拷贝问题的分析

延续上面出现的问题,本节进行分析,将上面的过程的内存四区图画出来:

C++编译器执行默认的拷贝构造函数,执行的是一个浅拷贝,即把对象obj1的属性(m_p,m_len),复制一份放到同样的内存区中,只是把指针变量的值和变量的值复制一份给obj2,没有把指针变量所指向的内存空间给重新开辟内存。这种拷贝只是把值给拷贝了, 没有把指针变量指向内存空间的数据也给拷贝了,这种拷贝就叫浅拷贝。所新生成的obj2,里的指针变量指向的内存空间还是原来的内存空间。当析构的时候,先析构obj2,再析构obj1,可因为析构obj2时候,把指向的内存空间给清除了,等到obj1析构的时候,提示原来的内存空间已经释放掉了,肯定不能重复释放了,所以,就报错了!

浅拷贝问题的解决方案

浅拷贝的问题一句话来概括就是两个指针变量指向同一块内存空间造成的错误。那么如何解决呢?这种情况,我们应该使用深拷贝。应该在生成新的指针变量的时候,同时生成新的指针变量所指向的内存空间。

写深拷贝的拷贝构造函数:

	//解决方案: 手工的编写拷贝构造函数 使用深copy
	Name(const Name& obj1)
	{
		m_len = obj1.m_len;
		m_p = (char *)malloc(m_len + 1);
		strcpy(m_p, obj1.m_p);
	}

这样的就是深拷贝了。

默认等号操作符执行赋值操作时也是浅拷贝

//对象析构的时候 出现coredump
void objplaymain()
{
	Name obj3("obj3");

	obj3 = obj1;  // C++编译器提供的 等号操作 也属 浅拷贝
}

 这也是一个浅拷贝,也会出现两个指针指向同一块儿内存空间,造成报错。想解决这种问题,我们必须显式的重载等号运算符才可以,至于具体的怎么做,这里制作抛砖,以后引玉。

这里简单叙述一下原理:

当新开一个obj3的时候,里面的指针变量存放的是一个不同于obj1指向的新的内存空间,而当用等号进行赋值操作的时候,就把obj3中指针变量存放的指针粗暴的改成了obj1中对应的值,即将obj1和obj3中的指针变量都指向了同一块的内存空间,而原理被obj3指向的空间,就被弄丢了,造成内存空间泄漏。 

总体代码

dm09_深拷贝和浅拷贝.cpp


#define  _CRT_SECURE_NO_WARNINGS 
#include <iostream>
using namespace std;

//

class  Name
{
public:
	Name(const char *myp)
	{
		m_len = strlen(myp);
		m_p =(char *) malloc(m_len + 1); //
		strcpy(m_p, myp);
	}

	//Name obj2 = obj1;
	//解决方案: 手工的编写拷贝构造函数 使用深copy
	Name(const Name& obj1)
	{
		m_len = obj1.m_len;
		m_p = (char *)malloc(m_len + 1);
		strcpy(m_p, obj1.m_p);
	}

	~Name()
	{
		if (m_p != NULL)
		{
			free(m_p);
			m_p = NULL;
			m_len = 0;
		}
	}
protected:
private:
	char *m_p ;
	int m_len; 
};

//对象析构的时候 出现coredump
void objplaymain()
{
	Name obj1("abcdefg");
	//Name obj2 = obj1;  //C++编译器提供的 默认的copy构造函数  浅拷贝
	Name obj3("obj3");

	obj3 = obj1;  // C++编译器提供的 等号操作 也属 浅拷贝
}

void main91()
{
	objplaymain();
	cout<<"hello..."<<endl;
	system("pause");
	return ;
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

using namespace std;
 
class Name
{
    public:
        Name(const char *myp)
        {
            m_len = strlen(myp);
            m_p = (char *) malloc (m_len + 1); //给指针分配内容空间
            strcpy(m_p, myp);
            }  
        //Name obj2 = obj1;
        //手动解决拷贝构造函数,使用深copy
        //指针的真正用武之地就是在运行时分配未命名的内存来储存值;c语言中使用malloc来分配,c++使用new运算符
         
        Name(const Name & obj1)
        {
            m_len = obj1.m_len;
            m_p = (char *)malloc(m_len + 1); //给指针分配内容空间
            strcpy(m_p, obj1.m_p);
         }
         ~Name()
         {
            if(m_p != NULL)
            {
                free(m_p);//释放的不是指针,而是指针指向的内容空间,如果是浅拷贝的话,则释放第二次的时候,容易报错。
                m_p = NULL;
                m_len = 0;
             }
         }
    protected:
    private:
        char *m_p;
        int m_len;
};
 
void objdisplaymain()
{
    Name obj1("abcdefg");
    Name obj2 = obj1; //默认的copy构造函数 C++编译器给我们提供的
}
//在程序调用结束时,会调用析构函数来回收内存,析构掉obj1和obj2两个对象。根据析构函数的操作,浅拷贝会出现bug
int main()
{
    objdisplaymain();
    cout << "hello..." << endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值