1.什麼是淺拷貝,裡面存在什麼問題?
如果已定義好一個類,如果類中隻有int、double、char、bool這類基本類型的變量,由于基本變量的所占空間是已知的,
是以在編譯時編譯器就可确定所需記憶體大小而進行配置設定(靜态配置設定記憶體),對象a和對象b他們的變量存在于各自的記憶體塊中(對象記憶體空間),淺拷貝後a的所有變量都被拷貝,如果a中的變量值被修改不會影響b内的變量值。
但假如這個類是個含有一個指針變量p,其中b對象在運作時配置設定了塊記憶體(比如動态建立了數組,或讀取了檔案資料流,即動态配置設定記憶體),p指向該記憶體,淺拷貝後,a的p被指派,是以同樣指向該記憶體塊,也就是說a的指針p指向了b的指針p指向的記憶體。這就存在一個問題,兩個對象的指針都指向一塊記憶體,如果通過a的指針p修改資料,顯然就會使得b内的資料也受到影響,這不符合解耦原則而破壞了對象的封裝性。
舉一個簡單的例子:
void FunTest1()
{
int *pTest1 = new int[10];
int *pTest2 = pTest1;
delete[] pTest1;
delete[] pTest2;
}
很明顯pTest1指向的空間被釋放了兩次,肯定會出問題,為了避免這種情況,就要用到深拷貝。
2.用深拷貝怎麼解決?深拷貝的兩種書寫方式:普通版和簡潔版
舉一個String類的例子
#define _CRT_SECURE_NO_WARNINGS 1
#include<string.h>
#include<iostream>
using namespace std;
class String
{
public:
//構造函數
String(const char *pStr = "")
{
if (NULL == pStr)
{
_pStr = new char[1];
*_pStr = '\0';
}
else
{
_pStr = new char[strlen(pStr) + 1];
strcpy(_pStr, pStr);
}
}
普通版主要是利用strcpy實作
//普通版
//拷貝構造函數
String(const String& s)
:_pStr(new char[strlen(s._pStr) + 1])
{
strcpy(_pStr, s._pStr);
}
//重載運算符=
String& operator=(const String& s)
{
if (this != &s)
{
char *pTmp = new char[strlen(s._pStr) + 1];
strcpy(pTmp, s._pStr);
delete[] _pStr;
_pStr = pTmp;
}
return *this;
}
簡潔版是利用swap函數
//簡潔版
//拷貝構造函數
String(const String& s)
:_pStr()
{
String tmp(s._pStr);
swap(_pStr, tmp._pStr);
}
//重載運算符=
String& operator=(const String& s)
{
if (this != &s)
{
String tmp(s);
swap(_pStr, tmp._pStr);
}
return *this;
}
3、在深拷貝版本的任何一個string類中,完成以下函數
#define _CRT_SECURE_NO_WARNINGS 1
#include<string.h>
#include<iostream>
using namespace std;
class String
{
public:
//構造函數
String(const char *pStr = "")
{
if (NULL == pStr)
{
_pStr = new char[1];
*_pStr = '\0';
}
else
{
_pStr = new char[strlen(pStr)+ 1];
strcpy(_pStr, pStr);
}
}
//普通版
//拷貝構造函數
//String(const String& s)
// :_pStr(new char[strlen(s._pStr) + 1])
//{
// strcpy(_pStr, s._pStr);
//}
重載運算符=
//String& operator=(const String& s)
//{
// if (this != &s)
// {
// char *pTmp = new char[strlen(s._pStr) + 1];
// strcpy(pTmp, s._pStr);
// delete[] _pStr;
// strcpy(_pStr, pTmp);
// }
// return *this;
//}
//簡潔版
//拷貝構造函數
String(const String& s)
:_pStr()
{
String tmp(s._pStr);
swap(_pStr, tmp._pStr);
}
//重載運算符=
String& operator=(const String& s)
{
if (this != &s)
{
String tmp(s);
swap(_pStr, tmp._pStr);
}
return *this;
}
//析構函數
~String()
{
if (_pStr != NULL)
{
delete[] _pStr;
_pStr = NULL;
}
}
size_t Size()const //計算字元串所占的位元組
{
char *tmp = _pStr;
int n = 0;
while (*tmp++)
{
n++;
}
n++; //包括\0
return n;
}
size_t Lengh()const //計算字元串長度
{
char *tmp = _pStr;
int n = 0;
while (*tmp++)
{
n++;
}
return n;
}
char& operator[](size_t index) //重載下标[]
{
char *tmp = _pStr + index;
return *tmp;
}
//比較字元串大小
bool operator>(const String& s)
{
if (strcmp(_pStr, s._pStr) > 0)
return true;
else
return false;
}
bool operator<(const String& s)
{
if (strcmp(_pStr, s._pStr) < 0)
return true;
else
return false;
}
bool operator==(const String& s)
{
if (strcmp(_pStr, s._pStr) == 0)
return true;
else
return false;
}
bool operator!=(const String& s)
{
if (strcmp(_pStr, s._pStr) != 0)
return true;
else
return false;
}
void Copy(const String& s)
{
delete[] _pStr;
_pStr = s._pStr;
}
//尋找子串
bool strstr(const String& s)
{
char *p1 = _pStr;
char *q = s._pStr;
char *p2 = p1;
while (*p2)
{
p2 = p1;
q = s._pStr;
while ((*p2 != '\0') && (*q != '\0'))
{
if (*p2++ == *q++)
{
;
}
else
{
p1++;
break;
}
}
if (*q == '\0')
{
return true;
}
}
return false;
}
//字元串連接配接,相似與strcat
String& operator+=(const String& s)
{
char *tmp = new char[strlen(s._pStr) + strlen(_pStr) + 1];
char *pTmp = tmp;
strcpy(tmp, _pStr);
char *tmp1 = s._pStr;
while (*tmp)
*tmp++;
while (*tmp1)
{
*tmp++ = *tmp1++;
}
*tmp = '\0';
*this = pTmp;
return *this;
}
void display()
{
cout << _pStr << endl;
}
private:
char* _pStr;
};
int main()
{
String s1("hello ");
String s2("world");
String s3("world");
s1 += s2;
s1.display();
s2.display();
s1.Copy(s2);
s1.display();
s2.display();
cout << s1[2] << endl;
s1.display();
cout << s1.Size() << endl;
cout << s1.Lengh() << endl;
if (s1 > s2)
{
cout << "s1>s2" << endl;
}
if (s1 < s2)
{
cout << "s1<s2" << endl;
}
if (s3 == s2)
{
cout << "s2=s3" << endl;
}
if (s1.strstr(s2))
cout << "有" << endl;
else
cout << "沒有" << endl;
system("pause");
return 0;
}
運作結果如下:
4.什麼是引用計數,用引用計數能解決淺拷貝存在的問題嗎?
在引用計數中,每一個對象負責維護對象所有引用的計數值。當一個新的引用指向對象時,引用計數器就遞增,當去掉一個引用時,引用計數就遞減。當引用計數到零時,該對象就将釋放占有的資源,就避免了淺拷貝中出現多次析構的問題。
5、對引用計數進行改進,完成string的引用計數版本。即寫時拷貝。(更新中)