条款28:避免返回handle指向对象内部成分

条款28:避免返回handle指向对象内部成分
        handle是指reference、指针和迭代器等都可以称之为handle,都是用来代表一个对象的另一种身份。
实现书中的坐标例子
namespace item28 {
	class Point{  
	public:  
		Point(int x, int y) :m_x(x), m_y(y) {}
		void setX(int x) {m_x = x;}
		void setY(int y) {m_y = y;}
	private:
		int m_x;
		int m_y;
	};  
	struct RectData{  
		RectData(const Point& pl, const Point& pr) :ulhc(pl), lrhc(pr) {}
		Point ulhc;  
		Point lrhc;  
	};  
	class Rectangle{  
	public:  
		Rectangle(const Point& pl, const Point& pr) 
		{
			pData = std::tr1::shared_ptr<RectData>(new RectData(pl, pr));
		}
		Point& upperLefter() const{return pData->ulhc;}  
		Point& lowerRight() const{return pData->lrhc;}    
	private:  
		std::tr1::shared_ptr<RectData> pData;
	};  
}
调用
	item28::Point p1(0, 0);
	item28::Point p2(1, 1);

	const item28::Rectangle rect(p1, p2);
	rect.upperLefter().setX(2);  //可以改变坐标的值
       upperLefter函数定义成const,本身是不会修改成员的值,但返回一个reference对象,指向成员变量,客户可通过外面修改。导致有下面两点问题
第一,成员变量的封装性最多只等于“返回其reference”的函数的访问级别。
第二,如果const成员函数传出一个reference,后者所指数据与对象自身有关,而它又被存储在对象之外,那么这个函数的调用者可以修改那笔数据。
       如果它们返回的是指针或迭代器,相同的结果还会发生,原因相同。reference、指针和迭代器统统都是所谓的handles,而返回一个“代表对象内部数据的handle”,随之而来的便是“降低对象封装性”的风险。同时,也可能造成“虽然调用const成员函数却造成对象状态被更改”。
       通常认为对象的“内部”就是指它的成员变量,其实不被公开使用的成员函数(protected或private)也是对象“内部”的一部分,所以也不该返回它们的handles
       上面两个问题可以通过在函数前加上const修饰解决。但还会出现另外一个问题
新增一个函数获取对象
const Rectangle GetRectangle(){return Rectangle(Point(2, 3), Point(4, 5));}
调用
	item28::Point p1(2, 0);
	item28::Point p2(1, 1);

	const item28::Rectangle rect(p1, p2);
	const item28::Point &pUpper = item28::GetRectangle().upperLefter();
        GetRecttangle调用获得一个新的、暂时的Rectangle对象,这个对象没有名称,权且称它为temp。随后upperLeft作用于temp对象身上,返回reference指向temp的一个内部成分。具体指向temp的那个Point对象。但是这个语句结束之后temp将被销毁,而那间接导致temp内的Points析构。最终导致pUpperLeft指向一个不再存在的对象;变成空悬、虚吊(dangling)!
        函数返回一个handle代表对象内部成分总是很危险的。
记住
避免返回handle(包括reference、指针、迭代器)指向对象内部。遵守这个条款可增加封装性,帮助const成员函数的行为像个const,并将发生“虚吊号码牌”的可能性降至最低。

猜你喜欢

转载自blog.csdn.net/hualicuan/article/details/28607949