下面的一些想法来自《Effective C++》中的 Item 28: 避免返回对象内部构件的“句柄” 。
避免返回对象内部构件的句柄(引用,指针,或迭代器(也算是指针))。
如果返回对象内部构建的句柄(引用、指针),当对象先于内部句柄析构后,内部句柄会“空悬”。这里的“空悬”即指针无意义的指向,会引起堆内存无法释放。
如果返回对象内部的对象,不会有问题,对象会自动析构。
我想:不光要避免返回对象内部构建的句柄,对于“动态”(堆)对象,也要谨慎,在释放“动态”对象之前,一定要释放其内部的句柄。
万一要对对象内部句柄操作,一定要获取到对象的指针或引用,如果使用临时变量只会改变临时变量的拷贝,并不会改变对象的本体。当返回一个临时变量的拷贝时只有值,没有地址!!!
避免返回对象内部构件的句柄(引用,指针,或迭代器(也算是指针))。
如果返回对象内部构建的句柄(引用、指针),当对象先于内部句柄析构后,内部句柄会“空悬”。这里的“空悬”即指针无意义的指向,会引起堆内存无法释放。
如果返回对象内部的对象,不会有问题,对象会自动析构。
我想:不光要避免返回对象内部构建的句柄,对于“动态”(堆)对象,也要谨慎,在释放“动态”对象之前,一定要释放其内部的句柄。
万一要对对象内部句柄操作,一定要获取到对象的指针或引用,如果使用临时变量只会改变临时变量的拷贝,并不会改变对象的本体。当返回一个临时变量的拷贝时只有值,没有地址!!!
避免返回对象内部构件的句柄,这样也会提高封装性,帮助 const 成员函数产生 cosnt 效果。
#include <iostream>
using namespace std;
class Point
{
public:
Point()
{
cout<<"Point()"<<endl;
}
Point(int x, int y)
{
cout<<"Point(int x, int y)"<<endl;
this->x = x;
this->y = y;
}
void setX(int newVal)
{
x = newVal;
}
void setY(int newVal)
{
y = newVal;
}
int &getX()
{
return x;
}
int getY()
{
return y;
}
~Point()
{
cout<<"~Point()"<<endl;
}
private:
int x;
int y;
};
struct RectData
{
Point ulhc;
Point lrhc;
};
class Rectangle
{
public:
Rectangle(){}
Rectangle(Point p1, Point p2):pData(0)
{
pData = new RectData;
pData->ulhc = p1;
pData->lrhc = p2;
}
//Point& upperLeft() const
//如果没有&,返回的是pData->ulhc的拷贝,而非pData->ulhc本身;后续对pData->ulhc的拷贝赋值,并不会改变它的本体
Point& upperLeft() const
{
return pData->ulhc;
}
//const Point& upperLeft() const
//返回const 类型,会使对象内部构建只读,提高封装性
Point& lowerRight() const
{
return pData->lrhc;
}
void Print() const
{
cout<<"upperLeft: "<<upperLeft().getX()<<" "<< upperLeft().getY()<<endl;
cout<<"lowerRight: "<<lowerRight().getX()<<" "<< lowerRight().getY()<<endl;
}
~Rectangle()
{
if( 0 != pData)
{
delete pData;
pData = 0;
}
}
//private:
RectData *pData;
};
const Rectangle boundingBox( )
{
Point coord1(0, 0);
Point coord2(100, 100);
const Rectangle rec(coord1, coord2);
return rec;
}
int main()
{
Point coord1(0, 0);
Point coord2(100, 100);
const Rectangle rec(coord1, coord2);
rec.Print();
rec.upperLeft().setX(50);
rec.upperLeft().setY(51);
rec.Print();
//调用会返回一个新建的临时的 Rectangle 对象。这个对象没有名字,所以我们就称它为 temp。
//于是 upperLeft 就在 temp 上被调用,这个调用返回一个引向temp的一个内部构件的引用,特别是,它是由Points构成的。
//在这个语句的末尾,boundingBox 的返回值temp被销毁了,这将间接导致 temp 的 Points 的析构。
//接下来,剩下 pUpperLeft 指向一个已经不再存在的对象;pUpperLeft 空悬在创建它的语句的末尾!
//这就是为什么任何返回一个对象的内部构件的句柄的函数都是危险的。
Point *pUpperLeft = &(boundingBox( ).upperLeft());
}