C++中的深浅拷贝以及string类的模拟实现
深浅拷贝
浅拷贝
浅拷贝也叫做位拷贝,实例化新对象调用拷贝构造函数时只是把原对象中的数据原封不动的拷贝一份放在新的对象中,这在没有涉及到资源管理的类中是没有问题的。
我们可以先来看一个日期类
class Date{
friend ostream& operator<<(ostream& _cout, const Date& d){
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
public:
Date(int year = 1970, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d){
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
int main()
{
Date t1(2018, 12, 7);
cout << t1 << endl;
Date t2;
cout << t2 << endl;
return 0;
}
使用默认生成的拷贝构造函数得到的两个对象中的数据是一样的,这在没有资源的类中是可以的,但是如果一个类中有资源,那还能这样拷贝吗,可以简单给出一个涉及资源的类来。
class String{
public:
String(const char* str = ""){
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
~String(){
if(_str){
delete[] _str;
_str = nullptr;
}
}
private:
char *_str;
};
int main()
{
String s1("hello");
String s2(s1);
return 0;
}
程序运行完毕会报错double free,那为什么会出现这种状况?
当使用类中默认的拷贝构造函数时,编译器只是将s1中存放的地址简单的给了s2,但是两个对象还是指向同一块内存空间,所以在释放s2资源后,s1不知道资源已经被释放,以为自身指向的资源还有效,所以再去释放s1就会报错。同样的,在进行赋值运算符重载时也是简单的把旧的对象中的数据拷贝至新的对象中。
所以在有资源管理的类中,拷贝构造函数和赋值运算符重载必须显式给出。为了解决浅拷贝中所存在的问题,C++提出了深拷贝的概念。
深拷贝
在涉及到资源管理的类中,显式给出拷贝构造函数,给每个对象独立分配资源,保证多个对象之间不会因为资源共享问题而导致程序崩溃。
写时拷贝
写时拷贝是在浅拷贝的基础上增加了计数器而扩展来的,在构造对象时,将资源计数器置成1,随后再创建对象是只要给计数器加加即可,如果需要赋值等其他的操作,再重新开辟空间。简单理解写时拷贝就是什么时候需要什么时候给空间
string类的简单实现
基于深浅拷贝我们可以实现一个简单的string类
class String{
public:
String(const char* str = ""){
if(str == nullptr){
str = ""; //如果str为空,给str一个空串
}
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
String(const String& s)
: _str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
}
String& operator=(const String& s){
if(this != &s){
//防止赋值左对象的空间小于右对象
char *pStr = new char[strlen(s._str) + 1];
strcpy(pStr, s._str);
delete[] _str;
_str = pStr;
}
return *this;
}
~String(){
if(_str){
delete[] _str;
_str = nullptr;
}
}
private:
char *_str;
};