个人技术分享

目录

浅拷贝 

深拷贝

写时拷贝 (了解)

引用计数

 swap实现拷贝

swap实现赋值

 


浅拷贝 

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共 享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

String(const char* str = "")
{
	// 构造String类对象时,如果传递nullptr指针,可以认为程序非
	if (nullptr == str)
	{
		assert(false);
		return;
	}
	_str = new char[strlen(str) + 1];
	strcpy(_str, str);
}

如果拷贝构造函数按照上述代码编写会发生什么?

96a1355fc28f4ec5a03152b9a3927da6.png

 可以看到s1和s2共用一块空间,s2只拷贝了s1的数据,没有属于自己的空间。

上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构 造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。


深拷贝

深拷贝:每个对象都有一份独立的资源,不要和其他对象共享。如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

为了优化上述拷贝构造函数,采用深拷贝方式,解决了共用空间的问题,代码如下:


    string(const string& s)
    :_str = new char[s._capacity + 1];
	{
		strcpy(_str, s._str);
	}

此时,系统又给s2新开了一个一样大的空间,将s1的值拷贝给s2,两者拥有各自独立的空间。

cdfa9efd273b4461986521d1b007ba6f.png


写时拷贝 (了解)

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。

引用计数

用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。


 swap实现拷贝

	// 修改前
    string::string(const string& s)
	{
		_str = new char[s._capacity + 1];
		strcpy(_str, s._str);
		_size = s._size;
		_capacity = s._capacity;
	}
    
    // 修改后
	string::string(const string& s)
	{
		string tmp(s._str);
		swap(tmp);
	}

79b397f688654036ad3653876617d941.png

虽然没有很大的时间和空间差别,但是利用swap可以提高代码可读性。


swap实现赋值

68f6078572d44dfeb5a1caa975c3ebca.png

与拷贝构造函数类似,也是用s._str构造一个tmp,然后进行swap;