虽然我们知道了拷贝构造函数,但是大多数简单程序中都不需要特别编写拷贝构造函数,隐含的拷贝构造函数足以实现对象间数据元素的一一对应复制。但是隐含的拷贝构造函数并不总是适用的,因为它完成的只是浅拷贝。
1.浅拷贝
【例】对象的浅拷贝
cpp
class Point
{
public:
Point() :x(0), y(0)
{
cout << "调用默认构造函数" << endl;//数组元素的地址
}
Point(int x, int y) :x(x), y(y)
{
cout << "调用构造函数" << endl;
}
~Point()
{
cout << "调用析构函数" << endl;
}
int getX()
{
return x;
}
int getY()
{
return y;
}
void move(int newX, int newY)
{
x = newX;
y = newY;
cout << "(" << x << "," << y << ")" << endl;//对应数组元素的地址
}
private:
int x, y;
};
//动态数组类
class Arr
{
public:
Arr(int size) :size(size)
{
points = new Point[size];
}
~Arr()
{
cout << "删除对象:" << endl;
delete[]points;
}
//获得下标为index的数组元素
Point& element(int index)
{
assert(index >= 0 && index < size);//如果数组下标越界,程序中止
return points[index];
}
private:
Point* points;//指向动态数组的首地址
int size;//数组大小
};
int main()
{
int count;
cout << "请输入点的个数:";
cin >> count;
Arr parr1(count);
parr1.element(0).move(1, 2);
parr1.element(1).move(2, 3);
parr1.element(2).move(3, 4);
Arr parr2(parr1);
cout << "从parr1拷贝过来的点:" << endl;
cout << "parr2的第一个元素:" << "(" << parr2.element(0).getX() << "," << parr2.element(0).getY() << ")" << endl;
cout << "parr2的第二个元素:" << "(" << parr2.element(1).getX() << "," << parr2.element(1).getY() << ")" << endl;
cout << "parr2的第三个元素:" << "(" << parr2.element(2).getX() << "," << parr2.element(2).getY() << ")" << endl;
cout << "更新parr1的内容:" << endl;
parr1.element(0).move(10, 20);
parr1.element(1).move(20, 30);
parr1.element(2).move(30, 40);
cout << "更新parr1的内容之后parr2的内容:" << endl;
cout << "parr2的第一个元素:" << "(" << parr2.element(0).getX() << "," << parr2.element(0).getY() << ")" << endl;
cout << "parr2的第二个元素:" << "(" << parr2.element(1).getX() << "," << parr2.element(1).getY() << ")" << endl;
cout << "parr2的第三个元素:" << "(" << parr2.element(2).getX() << "," << parr2.element(2).getY() << ")" << endl;
return 0;
}
运行结果:
结果分析:
程序中parr2的内容是从parr1中拷贝过来的,它们的初始状态是一样的,但是当程序通过move函数更新parr1中的第一组点之后,parr2中的第二组点也被更新到了同样的位置。这就说明两组点之间存在着某种必然的联系,而这种联系并不是我们所期望的。
这里建立对象parr2时调用的是隐含的默认拷贝构造函数,实现对应数据项的直接拷贝。如下图所示:
当程序中更新parr1中的点时,也影响到了parr2。这种效果就是"浅拷贝"。
浅拷贝还有很大的弊病,在程序结束之前parr1和parr2的析构函数会被自动调用,在把理想的结果输出完成后会连续不停止地一直输出"调用析构函数",动态内存的分配空间会被释放。由于两个对象共用了一块内存空间,因此该空间被释放了两次,于是导致运行错误。解决这一问题的方法是编写拷贝构造函数,实现"深拷贝"。
1.浅拷贝
【例】对象的深拷贝
cpp
class Point
{
public:
Point() :x(0), y(0)
{
cout << "调用默认构造函数" << endl;//数组元素的地址
}
Point(int x, int y) :x(x), y(y)
{
cout << "调用构造函数" << endl;
}
~Point()
{
cout << "调用析构函数" << endl;
}
int getX()
{
return x;
}
int getY()
{
return y;
}
void move(int newX, int newY)
{
x = newX;
y = newY;
cout << "(" << x << "," << y << ")" << endl;//对应数组元素的地址
}
private:
int x, y;
};
//动态数组类
class Arr
{
public:
Arr(const Arr& v);
Arr(int size) :size(size)
{
points = new Point[size];
}
~Arr()
{
cout << "删除对象:" << endl;
delete[]points;
}
//获得下标为index的数组元素
Point& element(int index)
{
assert(index >= 0 && index < size);//如果数组下标越界,程序中止
return points[index];
}
private:
Point* points;//指向动态数组的首地址
int size;//数组大小
};
Arr::Arr(const Arr& v)
{
size = v.size;
points = new Point[size];
for (int i = 0; i < size; i++)
{
points[i] = v.points[i];
}
}
int main()
{
int count;
cout << "请输入点的个数:";
cin >> count;
Arr parr1(count);
parr1.element(0).move(1, 2);
parr1.element(1).move(2, 3);
parr1.element(2).move(3, 4);
Arr parr2(parr1);
cout << "从parr1拷贝到parr2的点:" << endl;
cout << "parr2的第一个元素:" << "(" << parr2.element(0).getX() << "," << parr2.element(0).getY() << ")" << endl;
cout << "parr2的第二个元素:" << "(" << parr2.element(1).getX() << "," << parr2.element(1).getY() << ")" << endl;
cout << "parr2的第三个元素:" << "(" << parr2.element(2).getX() << "," << parr2.element(2).getY() << ")" << endl;
cout << "更新parr1的内容:" << endl;
parr1.element(0).move(10, 20);
parr1.element(1).move(20, 30);
parr1.element(2).move(30, 40);
cout << "更新parr1的内容之后parr2的内容:" << endl;
cout << "parr2的第一个元素:" << "(" << parr2.element(0).getX() << "," << parr2.element(0).getY() << ")" << endl;
cout << "parr2的第二个元素:" << "(" << parr2.element(1).getX() << "," << parr2.element(1).getY() << ")" << endl;
cout << "parr2的第三个元素:" << "(" << parr2.element(2).getX() << "," << parr2.element(2).getY() << ")" << endl;
return 0;
}
运行结果:
结果分析:
从这次的运行结果可以看出,程序实现的是深拷贝:更新parr1中的点不在影响parr2中的内粗空间,也不再引起错误。深拷贝的过程如图所示: