前言
今天在看《STL源码解析》并仿写queue
时,遇到一个我经常会混淆的问题,如下:
template <class T, deque<T> >
class queue {
……
public:
// 非const成员函数,返回数据的引用
reference front() { return c.front(); }
// const成员函数,返回数据的常量引用
const_reference front() const { return c.front(); }
……
};
为啥一个可以是非const的成员函数,而一个是const的成员函数。我想了一下,然后又想起了自己对const成员函数这一块儿一直不是很熟,故重新看了看《C++ primer》,大概懂了一些。
const成员函数与非const成员函数
在《C++ primer》中作者提到,每次调用成员函数,都会隐式传入一个指向对象的this指针。因为它是成员函数的隐式参数,所以我们无法修改this指针,使其在作为成员函数的参数传入时成为指向常量对象的指针。(当然this本身是一个常量指针,无法修改它保存的对象地址)那么如果常量对象调用非const的成员函数,就肯定会无法调用。因为this指针形参为Data * const
,也就是指向非常量对象的常量指针;而常量对象调用成员函数时,传入的实参是const Data* const
,形参与实参const类型不匹配,报错!所以才有了const成员函数。
const成员函数则修改了成员函数的隐式this指针形参,将其改成了const Data* const
,也就是指向常量对象的常量指针,这就使得常量对象可以成功调用这个成员函数。书上举了一个例子:
// 伪代码,说明隐式的this指针是如何使用的
// 下面的代码本身是非法的:因为我们不能显式地定义自己的this指针
// 谨记此处的this是一个指向常量的常量指针,因为isbn是一个常量成员
string Data::isbn(const Data* const this) {
return this->isbn;
}
在上例中因为this指针是指向常量的指针,所以常量成员函数不能改变调用它的对象的内容。isbn可以读取调用它的对象的数据成员,但是不能写入新值。
正因为const成员函数与非const成员函数在隐式this指针形参上const类型的不同,所以两者通过参数const类型的不同实现函数的重载。非常量对象虽然可以调用非const成员函数和const成员函数,但是非const成员函数是最佳匹配;而常量对象只能调用const成员函数。这也是为啥const成员函数能够实现函数重载的原因。
总结
所以,const成员函数通过修改传入成员函数的隐式this指针参数的const类型,限制传入的this指针必须是指向常量对象的指针,从而实现在const成员函数中无法通过this指针来修改对象的内容(成员的值等)。