所谓深复制,通俗的解释就是:不仅仅是复制数据的地址,更要复制数据本身
先来看一个不完善的类,如下:
#include<iostream>
#include<string>
using namespace std;
class StringBad
{
private:
char *str;//指向字符串的指针
int len;//当前字符串的长度
static int num;//记录创建的对象数量
public:
StringBad(const char*s);//自定义构造函数
StringBad(const StringBad& st);//复制构造函数
~StringBad();//析构函数
StringBad & operator=(const StringBad &st);//赋值运算符重载函数
};
StringBad::StringBad(const char*s)//自定义构造函数
{
len= strlen(s);
str = new char[len + 1];//当类中包含了使用new初始化的指针成员,应该定义一个复制构造函数(深复制)
strcpy(str, s);
num++;
}
StringBad::~StringBad()//析构函数
{
num--;
delete[]str; //析构函数中释放开辟的空间
};
int StringBad::num=0;
然后我们要清楚知道,程序什么时候调用复制(拷贝)构造函数,通常有3种情况:
①用已知对象作为实参,来初始化新对象
StringBad S1("Mike");
StringBad S2(S1);//此时会调用复制构造函数
②在创建对象的同时,用已知对象初始化。注意是同一时间
StringBad S1("Mike");
StringBad S2=S1;//在创建新对象S2的同时,进行初始化。这是最常见的,会调用复制构造函数的情况
** 需要区分:如果两个对象都已经存在,赋值的时候,调用的就是赋值运算符(重载)函数,如下
StringBad S1("Mike");
StringBad S2("Jerry");
S1=S2;//此时调用的运算符的重载函数,此时同样需要进行深复制
③某函数的参数类型为对象
void Show(StringBad S){......};
StringBad S1("Mike");
Show(S1);//实参对象将会传进来,以赋值的方式传递给形参,此时就会调用复制构造函数
那么问题就来了,StringBad类的复制构造函数应该怎么写?
众所周知,系统自动生成的,类的默认构造函数仅仅是简单的赋值,如下
StringBad(StringBad &S)
{
this->str=S.str;
this->len=S.len;
this->num=S.num;
}
不难看到,如此简单地将所有数据成员复制一遍,不仅没有把static类型变量数值更新,也没有把数据真正复制到新对象上来
所以,StringBad类正确的复制构造函数应该这样写
StringBad::StringBad(const StringBad&st)
{
this->str=new char[strlen(st.str)+1];//开辟与原对象长度一样空间,并用this->str指向
strcpy(this->str,st.str);//最关键一步,通过strcpy()函数进行数据的复制
this->len=st.len;
this->num++;//更新num变量数值
cout<<"调用了复制构造函数"<<endl;
}
同样需要注意“深复制”的,还有运算符重载函数:
StringBad& StringBad::operator=(const StringBad &st)
{
this->str=new char[strlen(st.str)+1];//开辟与原对象长度一样空间,并用this->str指向
strcpy(this->str,st.str);//最关键一步,通过strcpy()函数进行数据的复制
this->len=st.len;
cout<<"调用了运算符重载函数"<<endl;
return *this;//记得返回对象
}
主函数:
int main()
{
StringBad S0("Mike");//已知对象S0
StringBad S1("Jerry");//已知对象S1
StringBad S2(S0);//第一次调用复制构造函数
StringBad S3=S0;//第二次调用复制构造函数
S1=S0;//调用运算符重载函数
}