C++类——深浅拷贝

1.浅拷贝

浅拷贝字面意思就是浅层次的拷贝,就是简单的把值拷贝过去
下面我们来看一下浅拷贝的代码

class Base
{
public:
	Base(const char* str)
	{
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	Base(const Base& s)
		:_str(s._str)
	{

	}

	~Base()
	{
		if (_str)
		{
			delete[] _str;
		}
		_str = NULL;
	}
private:
	char* _str;
};
int main()
{
	Base s1("深浅拷贝");
	Base s2(s1);
	
	return 0}

但是这代码编译后就崩溃了,为什么呢?
我们在return 0;的前面加一个getchar(); 这样那个黑窗口就弹出来了,然后你一个回车就又崩溃了,证明return的时候出现了问题,return的时候干什么呢?执行析构函数,那 执行析构函数的时候为什么会出错?我们知道析构函数就是销毁内存的,那我们来看看s1,s2的内存

在这里插入图片描述
可以看到这俩个指针指向同一个内存但析构函数会调用俩次,那么在第二次调用的时候那块内存已经被销毁了,所以才会出错。
现在发现了浅拷贝的弊端,要解决这个问题就引入了新的概念——深拷贝
深拷贝的本质就是重新开辟一的空间来存放拷贝过来的数据
下面是深拷贝的代码

class Base
{
public:
	Base(const char* str)
	{
		_str = new char[strlen(str) + 1];
		//QuoteCount = new int(1);
		strcpy(_str, str);
	}
	Base(const Base& s)
		:_str(NULL)
	{
		Base tmp(s._str);

		swap(_str, tmp._str);
	}

	~Base()
	{
		if (_str)
		{
			delete[] _str;
		}
		_str = NULL;
	}
private:
	char* _str;
};

int main()
{
	Base s1("深浅拷贝");
	Base s2(s1);
	return 0;
}

这下就不会出错了,我们再来看一下s1,s2的内存
在这里插入图片描述
这下指针所指的内存就不一样了
可是深拷贝有的在使用的时候不断地开空间,使用完了又得销毁,十分费力,我们又在我们需要将拷贝过来的数据进行修改时不得不开辟出新的内存,所以就有了引入计数的写时拷贝,就是我们用数来记录有几个指针来指着这块内存,当想要销毁这块内存时将这个数减1,当这个数是1时才真正的销毁这块内存。
代码如下

class Base
{
public:
	Base(const char* str)
	{
		_str = new char[strlen(str) + 1];
		QuoteCount = new int(1);
		strcpy(_str, str);
	}
	Base(const Base& s)
		:_str(s._str)
		, QuoteCount(s.QuoteCount)
	{
		++(*QuoteCount);
	}
	~Base()
	{
		if (--(*QuoteCount) == 0)
		{
			delete[] _str;
			delete[] QuoteCount;
		}
	}

	void CopyOnWrite();
private:
	char* _str;
	int* QuoteCount;
};
void Base::CopyOnWrite()
{
	if (*QuoteCount > 1)
	{
		char* newstr = new char[strlen(_str) + 1];
		strcpy(newstr, _str);
		_str = newstr;
		QuoteCount = new int(1);
	}
}

这里为什么要用int型的指针呢?是因为这是一个int型的变量的话修改它时就会修改的不彻底,意思就是你在拷贝s2时将这个变量加1;那么只会改变s2的引用计数,s1的引用数据还是1,当我们通过地址改变数据时就不会出现这样的情况
我们看一下

int main()
{
	Base s1("");
	Base s2(s1);
	Base s3("");

	return 0;
}

在这里插入图片描述
可以看到s1,s2的引用计数是2,s3的引用计数是1.我们需要开辟新的地址是就调用CopyOnWrite()函数,这样就非常方便了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
设计并实现一个动态整型数组Vect,要求: (1)实现构造函数重载,可以根据指定的元素个数动态创建初始值为0的整型数组,或根据指定的内置整型数组动态创建整型数组。 (2)设计拷贝构造函数和析构函数,注意使用深拷贝。 (3)设计存取指定位置的数组元素的公有成员函数,并进行下标越界,若越界则输出“out of boundary”。 (4)设计获取数组元素个数的公有成员函数。 (5)设计用于输出数组元素的公有成员函数,元素之间以空格分隔,最后以换行符结束。 在main函数中按以下顺序操作: (1)根据内置的静态整型数组{1,2,3,4,5}构造数组对象v1,根据输入的整型数构造数组对象v2。 (2)调用Vect的成员函数依次输出v1和v2的所有元素。 (3)输入指定的下标及对应的整型数,设置数组对象v1的指定元素。 (4)根据数组对象v1拷贝构造数组对象v3。 (5)调用Vect的成员函数依次输出v1和v3的所有元素。 设计并实现一个动态整型数组Vect,要求: (1)实现构造函数重载,可以根据指定的元素个数动态创建初始值为0的整型数组,或根据指定的内置整型数组动态创建整型数组。 (2)设计拷贝构造函数和析构函数,注意使用深拷贝。 (3)设计存取指定位置的数组元素的公有成员函数,并进行下标越界,若越界则输出“out of boundary”。 (4)设计获取数组元素个数的公有成员函数。 (5)设计用于输出数组元素的公有成员函数,元素之间以空格分隔,最后以换行符结束。 在main函数中按以下顺序操作: (1)根据内置的静态整型数组{1,2,3,4,5}构造数组对象v1,根据输入的整型数构造数组对象v2。 (2)调用Vect的成员函数依次输出v1和v2的所有元素。 (3)输入指定的下标及对应的整型数,设置数组对象v1的指定元素。 (4)根据数组对象v1拷贝构造数组对象v3。 (5)调用Vect的成员函数依次输出v1和v3的所有元素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值