浅拷贝:编译器只是直接将指针的值拷贝过来,结果多个对象共用了一块内存,当一个对象调用了析构函数将这块内存释放掉之后,另一些对象不知道这块空间已经还给了系统,再次调用析构函数进行释放时发现已经释放了,就会造成程序崩溃。在类的成员中有指针类型的成员变量的时候,必须对其写出显式的拷贝构造函数和赋值运算符重载函数,否则,默认的拷贝构造函数和赋值运算符重载函数只会对该指针进行浅拷贝(即直接将指针的值拷贝过来),导致多个对象的指针变量实际上指的是同一块空间,会引发一系列的问题甚至是程序崩溃
class String
{
public:
String(char* ptr)
{
mptr = new char[strlen(ptr) + 1]();
strcpy_s(mptr, strlen(ptr) + 1, ptr);
}
~String()
{
delete[] mptr;
mptr = NULL;
}
private:
char* mptr;
};
int main()
{
String str1("hello");
String str2(str1);
return 0;
}
深拷贝:因为浅拷贝会引发种种的问题,所以这里就引入了深拷贝。深拷贝会在构造其余对象的时候,拷贝一块和被拷贝对象一样大的空间,并将空间内的内容拷贝过来。这样不同的对象就会指向不同的数据块。在涉及到类中有指针类型的变量时,尤其是要对该类进行相关拷贝的操作时,一定要显式的定义一个拷贝构造函数或是赋值运算符重载,进行深拷贝,不能用浅拷贝!!!
class String
{
public:
String(const char *str = NULL); 构造
String(const String &other); 拷贝构造
~String(void); 析构
String & operator = (const String &other); 赋值运算符重载
private:
char * m_data;
};
String::String(const char *str)
{
if( str == NULL)
{
m_data = new char[1];
*m_data = '/0';
}
else
{
int length = strlen(str);
m_data = new char[length+1];
strcpy(m_data,str);
}
}
String::~String(void)
{
delete [] m_data;
}String::String(const myString &other)
{
int length = strlen(other.m_data);
m_data = new char[length+1];
strcpy(m_data,other.m_data);
}String & String::operator = (const myString &other)
{
if(this == &other)
return *this;
delete [] m_data;
int length = strlen(other.m_data);
m_data = new char[length+1];
strcpy(m_data,other.m_data);
return *this;
}
int main()
{
char *a1 = "aaa";
char *a2 = "bbb";
String s3(a1);
const String s4(a2);
s3 = s4;
}
写时拷贝 : 写时拷贝,在复制一个对象的时候并不是真正的把原先的对象复制到内存的另外一个位置上,而是在新对象的内存映射表中设置一个指针,指向源对象的位置,并把那块内存的引用计数位加1。这样,在对新的对象执行读操作的时候,内存数据不发生任何变动,直接执行读操作;而在对新的对象执行写操作时,将真正的对象复制到新的内存地址中,并修改新对象的内存映射表指向这个新的位置,并在新的内存位置上执行写操作。
原理:采用引用计数的机制。当一个string对象str1构造时,string的构造函数会根据传入的参数在堆空间上分配内存,当有其他对象通过str1进行拷贝构造的时候,str1的引用计数会+1(即当有其他类需要这块内存的时候,引用计数+1)。当有对象析构时,这个引用计数会-1。直到最后一个对象析构时,引用计数为0,此时程序才会真正释放这块内存(前面的析构并没有释放该内存,而是让引用计数-1)。
class String
{
public:
String(char* ptr) //构造,多开辟四字节空间存放引用计数
{
mptr = new char[strlen(ptr) + 5]();
mptr += 4;
strcpy_s(mptr, strlen(ptr) + 1, ptr);
getRef() = 1;
}
String(const String& rhs) //拷贝构造,浅拷贝,加引用计数
{
mptr = rhs.mptr;
getRef()++;
}
String& operator=(const String& rhs) //赋值运算符的重载
{
if (this != &rhs)
{
--getRef();
if (getRef() == 0)
{
delete[] (mptr - 4);
}
mptr = NULL;
mptr = rhs.mptr;
getRef()++;
}
return *this;
}
~String() //析构,减引用计数,当等于零时释放内存
{
--getRef();
if (getRef() == 0)
{
delete[] (mptr - 4);
}
mptr = NULL;
}
char& operator[](int index) //写操作,旧的内存块,引用计数-1,开辟新地址,赋值,引用计数为1,
{
if (getRef() > 1)
{
--getRef();
char* ptmp = new char[strlen(mptr) + 5]();
ptmp += 4;
strcpy_s(ptmp, strlen(mptr) + 1, mptr);
mptr = ptmp;
getRef() = 1;
}
return mptr[index];
}
/* ’<<‘运算符的重载:
friend ostream& operator <<(ostream& _cout,const String& s);
ostream& operator<<(ostream& _cout, const String& s)
{
_cout << s._pstr;
return _cout;
}*/private:
int& getRef() //获取引用计数
{
return *(int*)(mptr - 4);
}
char* mptr; //指针
};
int main()
{
String str1("hello");
String str2("world");
String str3(str2);
str2[0] = 's';
return 0;
}