读书笔记_Effective_C++_条款二十八:避免返回handlers指向对象内部成分
举个例子:
1 class Student 2 { 3 private: 4 int ID; 5 string name; 6 public: 7 string& GetName() 8 { 9 return name; 10 } 11 };
这是一个学生的类,类里面有两个成员变量,一个是学生ID,用整数表示,另一个是姓名,用string表示。有一个公有的方法GetName(),获得学生的名字,根据条款20所说的,使用引用可以防止资源不必要地拷贝,那么在返回值这边就用string&。但现在问题来了,这个函数只是想返回学生的姓名,并不想用户对之进行修改,但返回引用却提供了这样的一个接口,如:
int main() { Student s; s.GetName() = "Jerry"; cout << s.GetName() << endl; }
就可以把名字进行修改。
你也许想到了,如果在前面加上const,像这样:
1 const string& GetName() 2 { 3 return name; 4 }
就可以阻止s.GetName() = “Jerry”这样的代码了。
但这样写还是存在问题,就是如果返回的引用生命周期比对象本身要长时,引用就会悬空,它会指向一个不存在的string。下面看一下“返回的引用生命周期比对象本身要长”的情况,这种情况还是很容易举出例子的,比如:
1 const string& fun() 2 { 3 return Student().GetName(); 4 } 5 6 int main() 7 { 8 string name = fun(); //name指向一个不存的对象的成员变量 9 }
这时候即使name读取不报错,也是一个巨大的隐患,因为它已经是虚吊(dangling)的了。
这就是为什么函数如果“返回一个handle代表对象内部成分”总是危险的原因,不在于返回值是不是const,而是在于如果handle(指针或引用)传出去了,就会暴露在“handle比其所指对象更长寿”的风险下。
但有些情况还是需要返回handle的,比如string或者vector里面的operator[],就是返回的引用,因为需要对这里面的元素进行操作。
好了,总结一下:
避免返回handles(包括reference、指针、迭代器)指向对象内部,遵守这个条款可增加封装性,并将发生dangling handles的可能性降至最低。如果有必要必须要返回handles,在编写代码时就一定要注意对象和传出handle的生命周期。