C++含有指针成员的类对象作为函数返回值
C++的函数内部如果声明并初始化了一个类的对象,并且在函数结束的时候要将这个对象返回,则实际的执行机制是首先调用该类的拷贝构造函数生成一个该对象的拷贝,然后返回这个拷贝,然后原始的对象会被析构。我之前一个以为是直接返回函数内部定义的变量,因此在这个过程中拷贝构造函数就显得十分重要,编译器会为我们生成一个默认的拷贝构造函数,但是当对象的类中含有指针成员的时候,这个默认的拷贝构造函数是错误的,不可能满足我们的要求,这时就要自己实现拷贝构造函数。
下面是一个例子,定义两个类,TPoint、TRect:
///TPoint类
class TPoint
{
public:
int* x;
int* y;
TPoint();
TPoint(const TPoint & pt);//拷贝构造函数的定义及实现
~TPoint();
};
TPoint::TPoint()
{
x = new int;
y = new int;
}
TPoint::TPoint(const TPoint & pt)//复制构造函数的定义及实现
{
printf("TPoint 拷贝构造函数被调用\n");
x = new int;
y = new int;
*x = *(pt.x);//按值拷贝pt中的变量,不能直接写x=pt.x,
//否则释放内存时会出错pt.x的内存会释放两次
*y = *(pt.y);//同上
}
TPoint::~TPoint()
{
if (x != NULL)
{
delete x;
x = NULL;
}
if (y != NULL)
{
delete y;
y = NULL;
}
printf("TPoint析构函数被调用 x,y deleted\n");
}
///TRect类
class TRect
{
public:
TPoint* point_ll;
TPoint* point_ur;
TRect();
TRect::TRect(const TRect & rect);
TRect(TPoint* point_ll, TPoint* point_ur);
~TRect();
};
TRect::TRect()
{
point_ll = new TPoint;
point_ur = new TPoint;
}
TRect::TRect(const TRect & rect)//拷贝构造函数的定义及实现
{
printf("TRect 拷贝构造函数被调用\n");
//此时会调用TPoint的拷贝构造函数TPoint(const TPoint & pt),
//按值的方式进行point_ll与rect.point_ll之间的拷贝
point_ll = new TPoint(*rect.point_ll);
point_ur = new TPoint(*rect.point_ur);
}
TRect::~TRect()
{
if (point_ll != NULL)
{
delete point_ll;
point_ll = NULL;
}
if (point_ur != NULL)
{
delete point_ur;
point_ur = NULL;
}
printf("TRect析构函数被调用 point_ll,point_ur deleted\n");
}
TRect point_test( )
{
TRect rectInside;
int x = 0;
int y = 0;
*(rectInside.point_ll->x) = x;
*(rectInside.point_ll->y) = y;
x = 10;
y = 10;
*(rectInside.point_ur->x) = x;
*(rectInside.point_ur->y) = y;
printf("rectInside内存地址:rectInside %p point_ll %p point_ll->x %p point_ll->y %p point_ur %p point_ur->x %p point_ur->y %p \nllx=%d; lly=%d; urx=%d; ury=%d;\n",
&rectInside, rectInside.point_ll,rectInside.point_ll->x, rectInside.point_ll->y, rectInside.point_ur, rectInside.point_ur->x, rectInside.point_ur->y,
*(rectInside.point_ll->x), *(rectInside.point_ll->y), *(rectInside.point_ur->x), *(rectInside.point_ur->x));
//return这一句时会先调用TRect的拷贝构造函数(拷贝构造函数内部会调用TPoint的拷贝构造函数)
//然后调用TRect的析构函数,析构rectInside
return rectInside;
}
void rect_test()
{
//这一句中的TRect rectOutside 接收的不是point_test()函数中定义的rectInside而是其拷贝构造后的对象
TRect rectOutside = point_test();
printf("rectOutside内存地址:rectOutside %p point_ll %p point_ll->x %p point_ll->y %p point_ur %p point_ur->x %p point_ur->y %p \nllx=%d; lly=%d; urx=%d; ury=%d;\n",
&rectOutside, rectOutside.point_ll, rectOutside.point_ll->x, rectOutside.point_ll->y, rectOutside.point_ur, rectOutside.point_ur->x, rectOutside.point_ur->y,
*(rectOutside.point_ll->x), *(rectOutside.point_ll->y), *(rectOutside.point_ur->x), *(rectOutside.point_ur->x));
//在函数退出时会析构rect
}
int main()
{
rect_test();
getchar();
return 0;
}
执行结果如下
可以看出rectInside和rectOutside的首地址以及point_ll、point_ur及其内部的x、y的指针的地址都不一样,说明拷贝构造成功了3-8行的拷贝构造和析构函数的调用是发生在point_test( )函数退出时的,就是“return rectInside;”这句触发了拷贝构造和析构。
最后3行的析构是rect_test()这个函数退出时析构rectOutside 对象时触发的