深浅拷贝(String类)

//

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//管理字符串
class String
{
public:
	String() :_pStr(new char('\0'))
	{}
	String(const char* pStr)
	{
		if (NULL == pStr)
			_pStr = new char('\0');
		else
		{
			_pStr = new char(strlen(pStr) + 1);
			strcpy(_pStr, pStr);
		}
	}
	~String()
	{
		if (_pStr)
		{
			delete[] _pStr;
			_pStr = NULL;
		}
	}
private:
	char* _pStr;
};
void Test()
{
	char* p = "hello";
	String s1;
	String s2(p);
	String s3("hello");
}
int main()
{
	Test();
	system("pause");
	return 0;
}


析构函数:弥补了销毁时使用delete[]还是delet

content:

   <1.0> //管理字符串---》反例

   <2.0>//浅拷贝

   <3.0>//深拷贝-->普通版

   <4.0>//深拷贝-->简洁版

   <5.0>//浅拷贝(引用计数版)_count普通类成员变量

   <6.0>//浅拷贝(引用计数版)_count static类成员变量

   <7.0>//浅拷贝(引用计数版)int* _count 类成员变量

   <8.0>浅拷贝(引用计数版)(模仿new)

扫描二维码关注公众号,回复: 2721110 查看本文章

<1.0>//管理字符串---》反例

代码一:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
	String(const char* pStr = " ")
	{
		if (NULL == pStr)
		{
			_pStr = new char[1];
			*_pStr = '\0';
		}
		else
		{
			_pStr = new char[strlen(pStr) + 1];
			strcpy(_pStr, pStr);
		}
	}
	~String()
	{
		if (_pStr)
		{
			delete[] _pStr;
			_pStr = NULL;
		}
	}
private:
	char* _pStr;
};
void Test()
{
	String s1("hello");
	String s2;
	String s3(NULL);
	String s4(s3);
}
int main()
{
	Test();
	system("pause");
	return 0;
}
结果如下:



由上图可以看出来:s3和s4共用同一空间,所以上面代码只是对字符串的表面操作内部却没有动。

<2.0>//浅拷贝:

代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
	String(const char* pStr = " ")
	{
		if (NULL == pStr)
		{
			_pStr = new char[1];
			*_pStr = '\0';
		}
		else
		{
			_pStr = new char[strlen(pStr) + 1];
			strcpy(_pStr, pStr);
		}
	}
	String(const String& s) :_pStr(s._pStr)
	{}
	String& operator=(const String& s) //
	{
		if (this != &s)
		{
			_pStr = s._pStr;
		}
		else
			return *this;
	}
	~String()
	{
		if (_pStr)
		{
			delete[] _pStr;
			_pStr = NULL;
		}
	}
private:
	char* _pStr;
};
void Test()
{
	String s1("hello");
	String s2;
	String s3(NULL);
	String s4(s3);
}
int main()
{
	Test();
	system("pause");
	return 0;
}
存在的问题:

1.0


2.0


<3.0>//深拷贝-->普通版:

代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
	String(const char* pStr = " ")
	{
		if (NULL == pStr)
		{
			_pStr = new char[1];
			*_pStr = '\0';
		}
		else
		{
			_pStr = new char[strlen(pStr) + 1];
			strcpy(_pStr, pStr);
		}
	}
	//拷贝构造函数一:
	String(const String& s) :_pStr(new char[strlen(s._pStr)+1])
	{
		strcpy(_pStr,s._pStr);
	}
	//拷贝构造函数二:
	//String(const String& s)//缺点:_pStr指向不明,野指针,非手动开辟,手动释放,出错。
	//{
	//	if (NULL != _pStr)
	//		delete[] _pStr;
	//	_pStr = new char[strlen(s._pStr) + 1];
	//	strcpy(_pStr, s._pStr);
	//}
	//赋值运算符重载:   1.0申请新空间,拷贝元素
	//                   2.0释放旧空间
	//                   3.0使用新空间(更改指向)
	//赋值运算符重载一:
	String& operator=(const String& s) 
	{                                  
		if (this != &s)                
		{
			char* pTemp = new char[strlen(s._pStr) + 1];
			strcpy(pTemp, s._pStr);
			delete[] _pStr;
			_pStr = pTemp;
		}
		else
			return *this;
	}
	//赋值运算符重载一:
	/*String& operator=(const String& s)   //缺点:直接释放空间,万一下一步开辟失败
	{                                      //也找不到原来指向的空间。
		if (this != &s)
		{
			delete[] _pStr;
			_pStr = new char[strlen(s._pStr) + 1];
			strcpy(_pStr, s._pStr);
		}
		else
			return *this;
	}*/
	~String()
	{
		if (_pStr)
		{
			delete[] _pStr;
			_pStr = NULL;
		}
	}
private:
	char* _pStr;
};
void Test()
{
	String s1("hello");
	String s2;
	String s3(NULL);
	String s4(s3);
}
int main()
{
	Test();
	system("pause");
	return 0;
}
<4.0>//深拷贝(简洁版)

代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
	String(const char* pStr = " ")
	{
		if (NULL == pStr)
		{
			_pStr = new char[1];
			*_pStr = '\0';
		}
		else
		{
			_pStr = new char[strlen(pStr) + 1];
			strcpy(_pStr, pStr);
		}
	}
	//拷贝构造函数一:
	/*String(const String& s)     //不足:得释放StrTemp._pStr(它不为NULL时)
	{                             //构造新对象时,_pStr指向的空间不明确没有给它手动开辟空间交换后。
		String StrTemp(s._pStr);  //直接调析构函数去释放StrTemp._pStr存在内存泄漏
		swap(_pStr,StrTemp._pStr);
	}*/
	//拷贝构造函数二:
	//String(const String& s)   //缺点:申请了空间未用
	//{
	//	_pStr = new char[];
	//	String StrTemp(s, _pStr);
	//	swap(_pStr, StrTemp._pStr);
	//}
	//拷贝构造函数三:
	String(const String& s) :_pStr(NULL)  //优点:不用释放StrTemp._pStr(它为NULL)
	{
		String StrTemp(s._pStr);
		swap(_pStr, StrTemp._pStr);
	}
	//赋值运算符重载一:
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			String StrTemp(s._pStr); //左侧调用构造函数 String StrTemp(s)调用拷贝构造函数;
			                         //调拷贝构造函数又得String StrTemp(s._pStr);代码冗余
			swap(_pStr, StrTemp._pStr);
		}
		return *this;
	}
	//赋值运算符重载二:
	//String& operator=(const String& s)//无法避免自己给自己赋值的情况
	//{
	//		String StrTemp(s._pStr); 
	//		swap(_pStr, StrTemp._pStr);
	//		return *this;
	//}
	//赋值运算符重载三:      
	//String& operator=(const String& s)//无法避免自己给自己赋值的情况,赋值完成后源的内容也变了
	//{                                 //违背了赋值的初衷:a=b,a的内容变,b内容不变
	//	swap(_pStr,s._pStr);
	//	return *this;
	//}
	~String()
	{
		if (_pStr)
		{
			delete[] _pStr;
			_pStr = NULL;
		}
	}
private:
	char* _pStr;
};
void Test()
{
	String s1("hello");
	String s2;
	String s3=s1;
	String s4(s3);
}
int main()
{
	Test();
	system("pause");
	return 0;
}
监视:


<5.0>//浅拷贝(引用计数版)_count普通类成员变量

代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
	String(const char* pStr = " ")
	{
		if (NULL == pStr)
		{
			_pStr = new char[1];
			*_pStr = '\0';
		}
		else
		{
			_pStr = new char[strlen(pStr) + 1];
			strcpy(_pStr, pStr);
		}
		_count = 1;
	}
	String(const String& s) :_pStr(s._pStr), _count(++s._count)
	{}
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			String StrTemp(s._pStr);
			swap(_pStr, StrTemp._pStr);
			swap(_count, StrTemp._count);
		}
		return *this;
	}
	~String()
	{
		if (_pStr&&--_count==0)
		{
			delete[] _pStr;
			_pStr = NULL;
		}
	}
private:
	char* _pStr;
	mutable int _count;
};
void Test()
{
	String s1("hello");
	String s2;
	String s3;
	s3= s1;
	String s4(s3);
}
int main()
{
	Test();
	system("pause");
	return 0;
}
第一次调析构函数


第二次调析构函数


清空s4时s3与s4共用 同一片空间,s3._count-1之后明显s3没有减一,所以之后这空间还会被销毁一次,内存泄露。

<6.0>//浅拷贝(引用计数版)_count static类成员变量

代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
	String(const char* pStr = " ")
	{
		if (NULL == pStr)
		{
			_pStr = new char[1];
			*_pStr = '\0';
		}
		else
		{
			_pStr = new char[strlen(pStr) + 1];
			strcpy(_pStr, pStr);
		}
		_count = 1;
	}
	String(const String& s) :_pStr(s._pStr)
	{
		_count = s._count;
		_count++;

	}
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			String StrTemp(s._pStr);
			swap(_pStr, StrTemp._pStr);
			swap(_count, StrTemp._count);
		}
		return *this;
	}
	~String()
	{
		if (_pStr&&--_count == 0)
		{
			delete[] _pStr;
			_pStr = NULL;
		}
	}
private:
	char* _pStr;
	static int _count;
};
int String::_count = 0;
void Test()
{
	String s1("hello");
	String s2(s1);
	String s3;
}
int main()
{
	Test();
	system("pause");
	return 0;
}
 String s1("hello");结果:

String s2(s1);结果:

String s3;结果:


这说明了将count设置为static型成员变量一旦构造新的类成员就会把count置为1,。与预期结果不符。

<7.0>//浅拷贝(引用计数版)int* _count 类成员变量

代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
	String(const char* pStr = " ") :_count(new int[1])
	{
		if (NULL == pStr)
		{
			_pStr = new char[1];
			*_pStr = '\0';
		}
		else
		{
			_pStr = new char[strlen(pStr) + 1];
			strcpy(_pStr, pStr);
		}
		*_count = 1;
	}
	String(const String& s) :_pStr(s._pStr)
	{
		_count = s._count;
		(*_count)++;
	}
	String& operator=(const String& s)
	{
		if (_pStr != s._pStr)   //避免:String s2(s1); String; s2=s1;的情况发生。

		{
			if (_pStr && 0 == --*_count)
			{
				delete[] _pStr;
				delete[] _count;
			}
			_pStr = s._pStr;
			++*(s._count);
			_count = s._count;
		}
		return *this;
	}
	~String()
	{
		if (_pStr&&--(*_count) == 0)
		{
			delete[] _pStr;
			_pStr = NULL;
			delete[] _count;
			_count == NULL;
		}
	}
private:
	char* _pStr;
	int* _count;
};
void Test()
{
	String s1("hello");
	String s2(s1);
	String s3;
	String s4(s3);
	s3 = s2;
}
int main()
{
	Test();
	system("pause");
	return 0;
}
结果:

缺点:需要维护两块空间(一大一小)

<8.0>浅拷贝(引用计数版)(模仿new)

代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
	String(const char* pStr = " ")
	{
		if (NULL == pStr)
		{
			_pStr = new char[1+4];
			_pStr += 4;
			*_pStr = '\0';
		}
		else
		{
			_pStr = new char[strlen(pStr) + 1+4];
			_pStr += 4;
			strcpy(_pStr, pStr);
		}
		GetRef() = 1;
	}
	String(const String& s) :_pStr(s._pStr)
	{
	   GetRef()++;
	}
	String& operator=(const String& s)
	{
		if (_pStr != s._pStr)   //避免:String s2(s1); String; s2=s1;的情况发生。

		{
			Release();
			_pStr = s._pStr;
			++GetRef();
		}
		return *this;
	}
	char& operator[](size_t index)
	{
		if (GetRef() > 1)
		{
			char* pTemp = new char[strlen(_pStr) + 1 + 4];
			*(int*)pTemp = 1;
			pTemp += 4;
			strcpy(pTemp, _pStr);
			--GetRef();
			_pStr = pTemp;
		}
		return _pStr[index];
	}
	const char& operator[](size_t index) const
	{
		return _pStr[index];
	}
	friend ostream& operator<<(ostream& _cout, const String& c);
	~String()
	{
		Release();
	}
private:
	char* _pStr;
	int& GetRef()
	{
		return *((int*)_pStr - 1);
	}
	void Release()
	{
		if (_pStr && 0 == --GetRef())
		{
			_pStr -= 4;
			delete[] _pStr;
			_pStr = NULL;
		}
	}
};
ostream& operator<<(ostream& _cout, const String& c)
{
	_cout << c._pStr;
	return _cout;
}
void Test()
{
	String s1("hello");
	String s2(s1);
	String s3;
	String s4(s3);
	s3 = s2;
	s1[2] = 'o';
	cout<<s1<< endl;
}
int main()
{
	Test();
	system("pause");
	return 0;
}
结果:



总结:

1. 浅拷贝:也称位拷贝,编译器只是直接将指针的值拷贝过来,当多个对象共用同一块内存,当一
个对象将这块内存释放掉之后,另一些对象不知道该块空间已经还给了系统,以为还有效,所以
在对这段内存进行操作的时候,发生了访问违规。

2.写时拷贝--Copy On Write。


猜你喜欢

转载自blog.csdn.net/WhiskyCocktail/article/details/78481407