这期内容当中小编将会给大家带来有关C++中有哪些拷贝方式,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
浅拷贝
浅拷贝就其他两种而言就相当的简单了,其编写的代码如下:
String::String(const String &s)
: _str(s._str)
, _size(s._size)
, _capacity(s._capacity)
{}
String & String::operator=(const String &s)
{
if (_str != s._str)
{
delete[] _str;
_str = s._str;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
虽然这种方法非常简单,但这种方法存在很大的漏洞,这种方法拷贝构造以及赋值运算符的重载后的对象与被拷贝的对象指向同一块空间,如果使用其中一个对象对其中的值进行修改时会导致另一个对象中的值也会发生改变,所以一般情况下不会使用浅拷贝这种拷贝方式。
但当对象中的值不能进行改变是一个const常量时,对象只可进行读不能进行修改,使用浅拷贝可减少内存的开销,不会有问题。
深拷贝
通过了解浅拷贝,我们发现浅拷贝的问题在于没有新空间的开辟所导致不能对对象中的值进行改变。但我们大部分的对象中的值都会需要进行修改,所以深拷贝就是为了解决这问题而出现的。既然指向空间相同就不能随意更改对象中内容,那么深拷贝就先给需要拷贝的对象开辟空间,再将内容拷贝过去。深拷贝分为传统写法以及现代写法这两种方法,其编写的代码如下:
class String
{
public:
String(const char* str = "");
String(const String& s);
String& operator=(const String& s);
~String();
private:
char* _str;
};
//传统写法完成String深拷贝
//String::String(const char* str)
// : _str(new char[strlen(str) + 1])
//{
// strcpy(_str, str);
//}
//
//String::String(const String& s) // 必须用引用否则会无限的递归
// : _str(new char[strlen(s._str) +1]) // 开辟s._str大小的空间,+1是因为‘/0’
//{
// strcpy(_str, s._str);
//}
//
//String& String::operator=(const String& s)
//{
// if (this != &s) // 判断是否相同
// {
// if(_str)
// delete[] _str; // _str不为空,则需要先释放空间
// _str = new char[strlen(s._str) + 1];
// strcpy(_str, s._str);
// }
// return *this;
//}
//
//String::~String()
//{
// if (NULL != _str)
// {
// delete[] _str;
// }
//}
//现代写法完成String深拷贝
String::String(const char* str)
: _str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
String::String(const String& s)
{
String tmp(s._str); // 用s._str定义一个tmp对象
swap(_str, tmp._str); // 将指针进行交换
}
String& String::operator=(String s) // 形参调用构造函数
{
swap(_str, s._str);
return *this;
}
String::~String()
{
if (NULL != _str)
{
delete[] _str;
}
}
相比较两种方法而言,更推荐现代写法,原因如下:
现代写法代码量相对而言较少,效率更高
现代写法的赋值运算符重载中调用了拷贝构造、拷贝构造中调用了构造函数,故现代写法中的复用性更强,更易于管理
传统写法中的赋值运算符重载会先释放原先开辟的空间,这样如果下面重新开辟空间失败不能拷贝时,那么原有数据不会被保留
深拷贝虽然会再次开辟空间增加内存的占用,但这样可以解决浅拷贝不能做到的问题。
写时拷贝
其实通过上面两种拷贝方式就可以解决很多问题,但浅拷贝不能修改,深拷贝如果不修改又占有了更多空间。因此写时拷贝就出现了,其实写时拷贝就是将上面两种方法的优点进行结合,通过引入一个引用计数来解决这些问题。
写时拷贝较上面方法更为复杂,所以在这里先进行说明:写时拷贝需要一个引用计数,因此要增加一个存放一个引用计数的位置,我们将这个位置放在_str前面的四个字节,每当拷贝一次就将引用计数++一次,进行修改时先判断一下引用计数是否为1,若为1则直接进行修改;否则先重新开辟一块空间,将原有引用计数–,再进行修改。
下面简单编写一下其代码:
class String
{
public:
String(char* str = "")
:_str(new char[(strlen(str) + 5)]) // 多开辟4个字节来存放引用计数
{
*((int*)_str) = 1;
_str += 4; // str中内容在引用计数后
strcpy(_str, str);
}
String(const String& s)
{
_str = s._str; // 基本与浅拷贝相似
++GetRefCount();
}
String& operator=(const String& s)
{
if (_str != s._str)
{
if (--(GetRefCount()) == 0)
{
delete[] _str;
}
_str = s._str;
++(GetRefCount());
}
return *this;
}
~String()
{
if (--(GetRefCount()) == 0)
{
delete[] (_str - 4);
}
}
int& GetRefCount() // 返回引用类型,也可写成指针类型
{
return *((int*)(_str - 4));
}
private:
char* _str;
};
上述就是小编为大家分享的C++中有哪些拷贝方式了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。