1===> 最简单的拷贝构造—浅拷贝
浅拷贝的最大问题就是它指向的是别人的空间,并不是自己的。在类进行析构的时候就会把别人的空间释放。所以我们有必要对其进行升级改造。
class My_string
{
public:
My_string(const char* str = "")//初始化构造
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
My_string(const My_string& s)//浅拷贝
:_str(s._str)
{}
My_string& operator=(const My_string& s)//浅拷贝赋值运算符重载
{
if (_str != s._str)
{
delete[]_str;
_str = s._str;
}
return *this;
}
~My_string()
{
if (_str)
{
delete[]_str;
}
}
private:
char* _str;
};
2===> 深拷贝
深拷贝时成员为自己独立的开出空间,然后把别人数据拷进自己的空间中,但是这种方法并不令人满意,它让我们自己手动的new空间。所以还可以进行改造。
class My_string
{
public:
My_string(const char* str = "")//初始化构造
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
My_string(const My_string& s)//深拷贝
:_str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
}
My_string& operator=(const My_string& s)//深拷贝赋值运算符重载
{
if (this != &s)
{
delete[]_str;
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str);
}
return *this;
}
private:
char* _str;
};
3===>剥削拷贝(现代拷贝)
我们把要拷贝的对象使用一个临时创建的变量(tmp),用初始化构造把要拷贝的对象初始化来tmp临时对象。然后我们把tmp交换给我们目标对象。这样我们就不用自己开空间了。不过在进行复制拷贝时要在初始化链表把目标对象置空(:_str(NULL)),这样可以避免tmp在析构的时候出现崩溃。不过这依旧算不上好的拷贝方式
class My_string
{
public:
My_string(const char* str = "")//初始化构造
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
My_string(const My_string& s)
:_str(NULL)
{
My_string tmp(s._str);
swap(_str, tmp._str);
}
My_string& operator=(const My_string& s)
{
if (this != &s)
{
delete[]_str;
_str = NULL;
My_string tmp(s._str);
swap(_str, tmp._str);
}
return *this;
}
~My_string()
{
if (_str)
{
delete[]_str;
}
}
private:
char* _str;
};
4===>写实拷贝
这个算是一种好的拷贝方式,在linux系统默认的拷贝方式就是写实拷贝,windows上是深拷贝。
写实拷贝的用处很多的,现在我们只在string里面说。
第一种写实拷贝(简单版)
我们给浅拷贝构造来上一个计数器,这样我们如果只想拷贝一下并不想修改我们并不需要开空间就不会浪费系统资源啦。如果我们要修改并且会影响到别人的空间,这样我们就把计数器减一就好了,代表着我们要修改的对象要独立起来与别人的空间划清界限(对象开出自己的空间)。这个计数器在简单版里是我们独立创建的一个对象用来计数。
class My_string
{
public:
My_string(const char* str = "")
:_str(new char[strlen(str) + 1])
, _pcount(new int[1])
{
strcpy(_str, str);
*_pcount = 1;
}
My_string(const My_string& s)
:_str(s._str)
{
_pcount = s._pcount;
(*_pcount)++;
}
My_string& operator=(const My_string& s)
{
if (_str != s._str)
{
if (--(*_pcount) == 0)
{
delete[]_str;
delete[]_pcount;
}
_str = s._str;
_pcount = s._pcount;
(*_pcount)++;
}
return *this;
}
~My_string()
{
if (--(*_pcount) == 0)
{
delete[]_str;
delete[]_pcount;
}
}
private:
char* _str;
int* _pcount;
};
第二种写实拷贝(稍微稍微有点难版)
这一种是我们把计数器放进对象中,我们要在对象中多开出4个字节的开空间,用强转的方式(((int*))来计数,这样我们就省去了对简单版计数对象的处理,全部在一个对象里处理。这种操作是系统处理的方式
class My_string
{
public:
My_string(const char* str = "")
:_str(new char[strlen(str) + 5])
{
_str += 4;
strcpy(_str, str);
GetRefCount() = 1;
}
int& GetRefCount()
{
return *((int*)(_str - 4));
}
My_string(const My_string& s)
:_str(s._str)
{
GetRefCount()++;
}
My_string& operator=(const My_string& s)
{
if (_str != s._str)
{
if (--GetRefCount() == 0)
{
delete[](_str - 4);
}
_str = s._str;
GetRefCount()++;
}
return *this;
}
~My_string()
{
if (--GetRefCount() == 0)
{
delete[](_str - 4);
}
}
private:
char* _str;
};