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;
}



运行结果如下:



浅拷贝与深拷贝并实现String_#include



4.什么是引用计数,用引用计数能解决浅拷贝存在的问题吗?
在引用计数中,每一个对象负责维护对象所有引用的计数值。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数到零时,该对象就将释放占有的资源,就避免了浅拷贝中出现多次析构的问题。



5、对引用计数进行改进,完成string的引用计数版本。即写时拷贝。(更新中)