浅拷贝:浅拷贝就是指不重新申请新的内存空间,仅仅是给原来被拷贝的量取了一个别名。这两名字都是共用的同一块内存空间。最典型的就是指针变量赋值。在通过一个指针修改了内存空间中的内容后,另外的指针也会发生相应改变。如下所示:
#include <iostream>
using namespace std;
void test1()
{
int* p1 = new int(1);
int* p2 = p1;
cout << "*p1 = " << *p1 << endl;
cout << "*p2 = " << *p2 << endl;
*p2 = 2;
cout << "after change *p2 to 2" << endl;
cout << "*p1 = " << *p1 << endl;
cout << "*p2 = " << *p2 << endl;
}
int main()
{
test1();
return 0;
}
输出结果为:
*p1 = 1
*p2 = 1
after change *p2 to 2
*p1 = 2
*p2 = 2
深拷贝:深拷贝是指拷贝过程中重新分配了新的内存空间存放拷贝的内容,通过拷贝后的变量名对内存空间中的内容进行修改,不影响原来的内存空间。最典型的是普通变量的赋值。如下所示:
int a = 1;
int b = a;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
b = 2;
cout << "after convert b to 2" << endl;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
输出结果为:
a = 1
b = 1
after convert b to 2
a = 1
b = 2
拷贝构造函数:
前面的内容都是铺垫,有了前面的认识,再进入拷贝构造函数就很容易理解了。由上面可知,浅拷贝会带来一定的问题,就是可以通过其他的变量访问、修改、甚至删除原先的内容和内存空间。这在指针和普通变量的复制上还看不出特别大的问题,但在有构造函数和析构函数的类中,必然会有问题。看下面的代码:
class Student
{
public:
string name;
int *score;
Student(){}
Student(string name, int inputscore[])
{
this->score=new int[2];
this->score[0]=inputscore[0];
this->score[1]=inputscore[1];
}
~Student()
{
delete [] score;
cout << "~Student() is called" << endl;
}
};
void test3()
{
int a1[2] = {80, 90};
Student stu1("stu1", a1);
cout << stu1.score[0] << endl;
cout << stu1.score[1] << endl;
}
输出结果为:
80
90
~Student() is called
这是没有问题的,下面我们再新建一个Student类的实例stu2,并用stu1给它赋值,代码如下:
int a1[2] = {80, 90};
Student stu1("stu1", a1);
cout << stu1.score[0] << endl;
cout << stu1.score[1] << endl;
Student stu2(stu1);
输出结果为:
80
90
~Student() is called
~Student() is called
这里问题就出现了,由于c++默认的拷贝构造函数是浅拷贝,所以意味着析构函数被调用一次之后,内存已经被释放,而stu2使用完成之后自动调用了自己的析构函数,释放了同一块内存。关于释放同一块内存的造成的后果及是否报错,可以参看我之前的博文。从现在的结果看,析构函数显然被调用了两次,释放了同一块内存,是一定不能被接受的。
所以,一旦我们要使用“=”给类的实例互相赋值,那么就需要手动写自己的拷贝构造函数,将带有指针的成员变量重新申请内存空间,普通变量不用(原因看看前面的例子就知道了)。代码如下:
class Student
{
public:
string name;
int *score;
Student(){}
Student(string name, int inputscore[])
{
this->score=new int[2];
this->score[0]=inputscore[0];
this->score[1]=inputscore[1];
}
Student(const Student& stu)
{
score = new int[2];
score[0] = stu.score[0];
score[1] = stu.score[1];
}
~Student()
{
delete [] score;
cout << "~Student() is called" << endl;
}
};
int a1[2] = {80, 90};
Student stu1("stu1", a1);
cout << stu1.score[0] << endl;
cout << stu1.score[1] << endl;
Student stu2(stu1);
stu2.score[0] = 85;
cout << stu1.score[0] << endl;
cout << stu2.score[0] << endl;
输出结果为:
80
90
80
85
~Student() is called
~Student() is called
这里虽然析构函数仍然调用了两次,但是明显释放的已经不是同一块内存了。