关于string类的深浅拷贝问题

             首先,先看一下下面的代码会出现什么问题?     

class String
{
public:
	String(const char* data = "")
	{
		if (NULL == data)
		{
			_data = new char[1];
			_data = "\0";
		}
		else
		{
			_data = new char[strlen(data) + 1];
			strcpy(_data, data);
		}
	}

	~String()
	{
		if (_data)
		{
			delete[] _data;
			_data = NULL;
		}
	}

	String(const String& s)
		:_data(s._data)
	{}
private:
	char* _data;
};

void test()
{
	String str1("hello world");
	String str2(str1);
}
       上面的程序会奔溃,让我们来分析原因:

         

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

       为了解决这个问题,我们引入深拷贝。

       

       

String::String(const String& s)//深拷贝
:_data(new char[strlen(s._data) + 1])
{
    strcpy(_data, s._data);
}

        上面代码则是str2,重新开辟了一块空间,并将str1里的值拷贝到str2这块空间中。

深拷贝中赋值运算符的重载: 

        下面有两种方法,我们来看看哪种方法比较。

         方法一:

	String& operator=(const String&s)
	{
		if (this != &s)
		{
			delete[] _data;
			_data = new char[strlen(s._data) + 1];
			strcpy(_data, s._data);
		}
		return *this;
	}
              方法二:

	String& operator=(const String&s)
	{
                if (this != &s)
		{
			char* tmp = new char[strlen(s._data) + 1];
			strcpy(tmp, s._data);
			delete[] _data;
			_data = tmp;
		}
		return *this;
	} 

       一般情况下,两种方法都可以,但第二种方法更优一些。

       方法一中,先将旧的空间释放掉,然后又重新开辟出与形参同样大小新的空间,然后将形参的内容拷贝到此空间,此方法有一个弊端,就是当空间申请失败时,不仅没有成功赋值,也破坏了原有被赋值的对象。

       方法二中,先开辟出新空间,将这个新空间赋值给tmp这个临时变量,就算开辟失败也不会破坏原有的对象。

       最后还有一点,就是这里的返回值是为了支持链式访问。

下面就是面试中String类深拷贝的写法,一般没有特殊要求,将必要的成员的成员函数给出就可以了。

class String
{
public:
	String(const char* data = "")
	{
		if (NULL == data)
		{
			_data = new char[1];
			_data = "\0";
		}
		else
		{
			_data = new char[strlen(data) + 1];
			strcpy(_data, data);
		}
	}

	~String()
	{
		if (_data)
		{
			delete[] _data;
			_data = NULL;
		}
	}
String(const String& s)
		:_data(new char[strlen(s._data)+1])
	{
		strcpy(_data, s._data);
	}

	String& operator=(const String&s)
	{
		if (this != &s)
		{
			char* tmp = new char[strlen(s._data) + 1];
			strcpy(tmp, s._data);
			delete[] _data;
			_data = tmp;
		}
		return *this;
	}

private:
	char* _data;
};
          上面所写的拷贝构造函数和赋值运算符重载函数属于传统写法,下面我们一起来看看它们的现代写法:

      拷贝构造函数的现代写法:

      

	String(const String& s)
		:_data(NULL)
	{
		String strtmp(s._data);
		std::swap(strtmp._data,_data);
	}
        

       赋值运算符重载函数的两种现代写法:

      

	String& operator=(const String&s)
	{
		std::swap(_data,s._data);
		return *this;//为了支持链式访问
	}

	String& operator=(const String&s)
	{
		if (this->_data != s._data)
		{
			String tmp(s);
			std::swap(tmp._data, _data);
		}
		return *this;//为了支持链式访问
	}
        两种写法不同的是,第一个的拷贝构造函数是在参数列表中完成的,第二种是在函数体内完成的。

      以上是关于String类的深浅拷贝问题,当然在面试中也可以写现代写法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值