浅拷贝与深拷贝并实现String

1.什么是浅拷贝,里面存在什么问题?

如果已定义好一个类,如果类中只有int、double、char、bool这类基本类型的变量,由于基本变量的所占空间是已知的, 所以在编译时编译器就可确定所需内存大小而进行分配(静态分配内存),对象a和对象b他们的变量存在于各自的内存块中(对象内存空间),浅拷贝后a的所有变量都被拷贝,如果a中的变量值被修改不会影响b内的变量值。
 
但假如这个类是个含有一个指针变量p,其中b对象在运行时分配了块内存(比如动态创建了数组,或读取了文件数据流,即动态分配内存),p指向该内存,浅拷贝后,a的p被赋值,因此同样指向该内存块,也就是说a的指针p指向了b的指针p指向的内存。这就存在一个问题,两个对象的指针都指向一块内存,如果通过a的指针p修改数据,显然就会使得b内的数据也受到影响,这不符合解耦原则而破坏了对象的封装性。
举一个简单的例子:
void FunTest1()
{
	int *pTest1 = new int[10];
	int *pTest2 = pTest1;
	delete[] pTest1;
	delete[] pTest2;
}
很明显pTest1指向的空间被释放了两次,肯定会出问题,为了避免这种情况,就要用到深拷贝。

2.用深拷贝怎么解决?深拷贝的两种书写方式:普通版和简洁版
举一个String类的例子
#define _CRT_SECURE_NO_WARNINGS 1 
#include<string.h>
#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);
		}
	}
普通版主要是利用strcpy实现
//普通版
	//拷贝构造函数
	String(const String& s)
		:_pStr(new char[strlen(s._pStr) + 1])
	{
		strcpy(_pStr, s._pStr);
	}

	//重载运算符=
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			char *pTmp = new char[strlen(s._pStr) + 1];
			strcpy(pTmp, s._pStr);
			delete[] _pStr;
			_pStr = pTmp;
		}
		return *this;
	}
简洁版是利用swap函数
//简洁版
	//拷贝构造函数
	String(const String& s)
		:_pStr()
	{
		String tmp(s._pStr);
		swap(_pStr, tmp._pStr);
	}

	//重载运算符=
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			String tmp(s);
			swap(_pStr, tmp._pStr);
		}
		return *this;
	}

3、在深拷贝版本的任何一个string类中,完成以下函数

#define _CRT_SECURE_NO_WARNINGS 1 
#include<string.h>
#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& operator=(const String& s)
	//{
	//	if (this != &s)
	//	{
	//		char *pTmp = new char[strlen(s._pStr) + 1];
	//		strcpy(pTmp, s._pStr);
	//		delete[] _pStr;
	//		strcpy(_pStr, pTmp);
	//	}
	//	return *this;
	//}

	//简洁版
	//拷贝构造函数
	String(const String& s)
		:_pStr()
	{
		String tmp(s._pStr);
		swap(_pStr, tmp._pStr);
	}

	//重载运算符=
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			String tmp(s);
			swap(_pStr, tmp._pStr);
		}
		return *this;
	}

	//析构函数
	~String()
	{
		if (_pStr != NULL)
		{
			delete[] _pStr;
			_pStr = NULL;
		}
	}

	size_t Size()const                       //计算字符串所占的字节
	{
		char *tmp = _pStr;
		int n = 0;
		while (*tmp++)
		{
			n++;
		}
		n++;                          //包括\0
		return n;
	}
                                
	size_t Lengh()const                          //计算字符串长度
	{
		char *tmp = _pStr;
		int n = 0;
		while (*tmp++)
		{
			n++;
		}
		return n;
	}

	char& operator[](size_t index)                     //重载下标[]
	{
		char *tmp = _pStr + index;
		return *tmp;
	}
         //比较字符串大小
	bool operator>(const String& s)                    
	{
		if (strcmp(_pStr, s._pStr) > 0)
			return true;
		else
			return false;
	}

	bool operator<(const String& s)
	{
		if (strcmp(_pStr, s._pStr) < 0)
			return true;
		else
			return false;
	}

	bool operator==(const String& s)
	{
		if (strcmp(_pStr, s._pStr) == 0)
			return true;
		else
			return false;
	}

	bool operator!=(const String& s)
	{
		if (strcmp(_pStr, s._pStr) != 0)
			return true;
		else
			return false;
	}

	void Copy(const String& s)
	{
		delete[] _pStr;
		_pStr = s._pStr;
	}
         //寻找子串
	bool strstr(const String& s)
	{
		char *p1 = _pStr;            
		char *q = s._pStr;            
		char *p2 = p1;            
		while (*p2)
		{
			p2 = p1;
			q = s._pStr;
			while ((*p2 != '\0') && (*q != '\0'))
			{
				if (*p2++ == *q++)
				{
					;
				}
				else
				{
					p1++;
					break;
				}
			}
			if (*q  == '\0')
			{
				return true;
			}
		}
		return false;
	}

            //字符串连接,相似与strcat
	String& operator+=(const String& s)
	{
		char *tmp = new char[strlen(s._pStr) + strlen(_pStr) + 1];
		char *pTmp = tmp;
		strcpy(tmp, _pStr);
		char *tmp1 = s._pStr;
		while (*tmp)
			*tmp++;
		while (*tmp1)
		{
			*tmp++ = *tmp1++;
		}
		*tmp = '\0';
		*this = pTmp;
		return *this;
	}

	void display()
	{
		cout << _pStr << endl;
	}
private:
	char* _pStr;
};

int main()
{
	String s1("hello ");
	String s2("world");
	String s3("world");

	s1 += s2;
	s1.display();
	s2.display();

	s1.Copy(s2);
	s1.display();
	s2.display();

	cout << s1[2] << endl;
	s1.display();

	cout << s1.Size() << endl;
	cout << s1.Lengh() << endl;

	if (s1 > s2)
	{
		cout << "s1>s2" << endl;
	}
	if (s1 < s2)
	{
		cout << "s1<s2" << endl;
	}
	if (s3 == s2)
	{
		cout << "s2=s3" << endl;
	}

	if (s1.strstr(s2))
		cout << "有" << endl;
	else
		cout << "没有" << endl;

	system("pause");
	return 0;
}
运行结果如下:
4.什么是引用计数,用引用计数能解决浅拷贝存在的问题吗?
在引用计数中,每一个对象负责维护对象所有引用的计数值。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数到零时,该对象就将释放占有的资源,就避免了浅拷贝中出现多次析构的问题。
5、对引用计数进行改进,完成string的引用计数版本。即写时拷贝。(更新中)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值