这是我在阅读Effective c++中认为比较重要的部分,下面给出了我对这一节的理解,并写出对应的比较容易理解的代码。
注意以下代码中,外界通过调用B类的get函数可以修改B类中指针指向的成员,而get函数是个const,但其实那部分数据应该是不能改变的。
class A {
public:
A(string&s) {
a = s;
}
void set(string&s) {
a = s;
}
void get() {
cout << a << endl;
}
private:
string a;
};
class B {
public:
B(A*a):b(a){}
A& get()const { //不安全的const函数
return *b.get();
}
private:
shared_ptr<A>b = nullptr;
};
int main() {
string s = "lkq";
A a(s);
B b(&a);
A &c = b.get();
c.set(string("fffff"));
a.get(); //打印fffff
return 0;
}
而这种情况只需要在const成员函数返回值前加const即可解决
const A& get()const
上述例子中const成员函数返回值的类型是引用,如果它们返回的是指针或迭代器,相同的情况还是会发生。引用、指针和迭代器统统都是所谓的handles(号码牌,用来取得某个对象)
上述代码还有一个很危险的问题,例如下面的代码:
const A* a = &B(&A(string("ssss"))).get();
a得到的是一个临时对象的地址,而在这条语句返回后,临时对象会被销毁,最终导致a指向一个不存在的对象!
请记住
避免返回handles指向对象的内部。遵守这个条款可增加封装性,帮助const成员函数更加像一个const,并将“虚号码牌“的可能性降低到最低。