这个设计可以编译,但它是错误的。实际上,它是自相矛盾的。一方面,upperLeft 和 lowerRight 是被声明为 const 的成员函数,因为它们被设计成仅仅给客户提供一个获得 Rectangle 的点的方法,而不允许客户改变这个 Rectangle。另一方面,两个函数都返回引向私有的内部数据的引用——调用者可以利用这些引用修改内部数据!例如: Point coord1(0, 0);
Point coord2(100, 100);
const Rectangle rec(coord1, coord2); // rec is a const rectangle from // (0, 0) to (100, 100)
rec.upperLeft().setX(50); // now rec goes from // (50, 0) to (100, 100)! | 请注意这里,upperLeft 的调用者是怎样利用返回的 rec 的内部 Point 数据成员的引用来改变这个成员的。但是 rec 却被期望为 const!
这直接引出两条经验。第一,一个数据成员被封装,但是具有最高可访问级别的函数还是能够返回引向它的引用。在当前情况下,虽然 ulhc 和 lrhc 被声明为 private,它们还是被有效地公开了,因为 public 函数 upperLeft 和 lowerRight 返回了引向它们的引用。第二,如果一个 const 成员函数返回一个引用,引向一个与某个对象有关并存储在这个对象本身之外的数据,这个函数的调用者就可以改变那个数据(这正是二进制位常量性的局限性的一个副作用)。
我们前面做的每件事都涉及到成员函数返回的引用,但是,如果它们返回指针或者迭代器,因为同样的原因也会存在同样的问题。引用,指针,和迭代器都是句柄(handle)(持有其它对象的方法),而返回一个对象内部构件的句柄总是面临危及对象封装安全的风险。就像我们看到的,它同时还能导致 const 成员函数改变了一个对象的状态。 |