淺拷貝是指當對象的字段值被拷貝時,字段引用的對象不會被拷貝。例如,如果一個對象有一個指向字符串的字段,並且我們對該對象做了一個淺拷貝,那麼兩個對象將引用同一個字符串。
存在問題:
如果源程序中沒有顯示定義拷貝構造函數,在進行對象的拷貝時,將調用系統默認的拷貝構造函數,這就使得兩個對象指向了同一資源,而析構函數又在對象生命週期結束後可以釋放空間,勢必會兩次返還空間,編譯器就會報錯。
這時就需要用到深拷貝了。
深拷貝的兩種版本:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//#if 0
class String
{
public:
String(char *pStr = "") //構造函數
{
if (NULL == pStr)
{
_pStr = new char[1];
*_pStr = 0;
}
else
{
_pStr = new char[strlen(pStr) + 1];
strcpy(_pStr, pStr);
}
}
//簡潔版
////拷貝構造函數 1
//String(const String &s)
// :_pStr(new char[strlen(s._pStr) + 1])
//{
// String temp(s._pStr);
// swap(_pStr, temp._pStr);
//}
////拷貝構造函數 2
//String(const String &s)
// :_pStr(NULL)
//{
// _pStr = new char[1];
// String temp(s._pStr);
// swap(_pStr, temp._pStr);
//}
//普通版
String(const String &s)
:_pStr(new char[strlen(s._pStr) + 1])
{
strcpy(_pStr, s._pStr);
}
//賦值運算符重載
/*
//由於pTemp在棧上,出了函數將銷燬,故錯誤
String &operator=(const String &s)//賦值運算符重載
{
if (this != &s)
{
char *pTemp = new char[strlen(s._pStr) + 1];// pTemp在棧上
strcpy_s(pTemp, strlen(s._pStr), s._pStr);
//strcpy_s(_pStr, strlen(pStr), pStr);
delete[] _pStr;
_pStr = pTemp;
}
return *this;
}
*/
//賦值運算符重載 1
/*
String &operator=(const String &s)
{
//if (this != &s) 賦值運算符重載的反例,會造成一塊空間多次釋放
//{
// _pStr = s._pStr;
//}
//return *this;
if (this != &s)
{
String temp(s._pStr);//用S裏面的字符串構造一個臨時的temp
swap(_pStr, temp._pStr);//交換兩個指針
}
return *this;
}
*/
//賦值運算符重載 2
String& operator=(const String& pStr)
{
if (this != &pStr)
{
//防止new開闢內存失敗,丟失原來內存
char* ptr = new char[strlen(pStr._pStr) + 1];
strcpy(ptr, pStr._pStr);
delete[]_pStr;
_pStr = ptr;
}
return *this;
}
~String() //析構函數
{
if (this != NULL)
{
delete[] _pStr;
_pStr = NULL;//可有可無
}
}
friend ostream& operator<<(ostream & _cout, const String&pStr);
private:
char *_pStr;
};
//輸出流重載
ostream& operator<<(ostream & _cout, const String&pStr)
{
_cout << pStr._pStr << endl;
return _cout;
}
void FunTest1()
{
String s1("hello");
cout << "s1=" << s1 << endl;
String s2(s1); //調用拷貝構造函數
cout << "s2=" << s2 << endl;
}
void FunTest2()
{
String s3("word");
String s4;
s4 = s3; //調用賦值運算符重載
cout << "s3=" << s3 << endl;
cout << "s4=" << s4 << endl;
}
int main()
{
FunTest1();
FunTest2();
getchar();
return 0;
}
//#endif
1、開闢兩個空間的計數,代碼如下:
#include<iostream>
using namespace std;
class String
{
public:
String(const char * pStr = "")//構造函數
:_pCount(new int(1)) //_pCount初始化爲1
{
if (NULL == pStr)
{
_pStr = new char[1];
*_pStr = '\0';
}
else
{
_pStr = new char[strlen(pStr) + 1];
strcpy(_pStr, pStr);
}
}
String(const String &s)//拷貝構造函數
:_pStr(s._pStr)
, _pCount(s._pCount)
{
++*_pCount;//調用一次拷貝構造函數_pCount++
}
//賦值運算符重載
String & operator = (const String & s)
{
if (this != &s)
{
if (0 == --*_pCount)
{
delete[] _pStr;
delete _pCount;
}
_pStr = s._pStr;
_pCount = s._pCount;
++*_pCount;
}
}
~String()//調用析構函數時 先--_pCount,判斷是否爲0
{
if ((_pStr) && (0 == --*_pCount))
{
delete[] _pStr;
_pStr = NULL;
delete _pCount;
_pCount = NULL;
}
}
friend ostream& operator<<(ostream & output, const String &s);
private:
char * _pStr;
int * _pCount;
};
ostream& operator<<(ostream & output, const String &s)//輸出流的重載
{
output << s._pStr << endl;
return output;
}
void FunTest()
{
String s1("hello");
cout << "s1=" << s1 << endl;
String s2(s1);
cout << "s2=" << s2 << endl;
String s3("heihei");
cout << "s3=" << s3 << endl;
String s4 = s3;
cout << "s4=" << s4 << endl;
}
int main()
{
FunTest();
getchar();
return 0;
}
運行結果:
2、一個空間的計數,代碼如下:
class String
//{
//public:
// String(char* ptr = NULL)
// {
// if (ptr == NULL)
// {
// _ptr = new char[5];
// *(_ptr + 4) = 0;
// }
// else
// {
// _ptr = new char[strlen(ptr) + 5];
// strcpy(_ptr + 4, ptr);
// }
// *(int*)_ptr = 1;
// }
// String(const String& s)
// :_ptr(s._ptr)
// {
// (*(int*)_ptr)++;
// }
// String& operator=(const String& s)
// {
// if (&s != this)
// {
// _ptr = s._ptr;
// (*(int*)_ptr)++;
// }
// return *this;
// }
// ~String()
// {
// if (--*(int*)_ptr == 0)
// {
// delete[] _ptr;
// _ptr = NULL;
// }
// }
//private:
// char* _ptr;
//};
class String
{
public:
String(const char* pStr)
{
if (pStr == NULL)
pStr = " ";
_pStr = new char[strlen(pStr) + 1 + 4];//多開闢4個字節
_pStr += 4;
strcpy(_pStr, pStr);
GetReference() = 1;
}
String(const String&s)
:_pStr(s._pStr)
{
++ GetReference();
}
String & operator = (const String & s)
{
if (this != &s)
{
if (--GetReference() = 0)
{
_pStr -= 4;
delete[] _pStr;
}
_pStr = s._pStr;
++GetReference();
}
}
~String()
{
if ((--GetReference() == 0) && _pStr)
{
_pStr -= 4;
delete[] _pStr;
_pStr = NULL;
}
}
friend ostream & operator<<(ostream &output, String &s);
private:
int& GetReference()
{
return *(int*)(_pStr - 4);
}
char* _pStr;
};
ostream & operator<<(ostream &output, String &s)
{
output << s._pStr << endl;
return output;
}
void FunTest()
{
String s1("ximenchuixue");
cout << "s1=" << s1 << endl;
String s2(s1);
cout << "s2=" << s2 << endl;
String s3("sikongzhaixing");
cout << "s3=" << s3 << endl;
String s4 = s3;
cout << "s4=" << s4 << endl;
}
int main()
{
FunTest();
getchar();
return 0;
}
運行結果: