关于const成员函数的介绍,我们先从类的成员函数一步步的来引入,在上一节中提到了const的一个用处:const可以修饰函数的参数和返回值,甚至函数的定义体。被const修饰的东西受到强制保护,可以预防意外的变动,能提高程序的健壮性。
这个用处大体上可以理解以下几点:
(1)可以修饰函数的参数和返回值。
(2)修饰函数的定义体,即定义类中的某个函数为恒态函数,也就是不可改变类中的数据成员。
上面两点充分的说明了const在类中的运用,那么以如下的例子来分析:
class Sales_item{
public:
double avg_price() const;
bool same_isbn(const Sales_item &rhs) const
private:
std::string isbn;
unsigned units_sold;
double revenue;
}
上面的代码中涉及到两处const:(1)avg_price函数的形参是空的,返回double型
(2)same_isbn 函数返回bool对象,有一个const Sales_item 类型的引用形参。
问题:如何理解形参表后面的const?
分析:在解释形参表后面的const时,首先我们要对成员函数函数体的定义有一个很深的了解,那先对成员函数函数体的定义做一个简单的说明。
-------------------------------------定义成员函数的函数体--------------------------------------------
1、首先关于类与其成员有以下几点要注意:
(1)类的所有声明必须在类定义的花括号内声明,此后,就不再为类增加任何成员。
(2)类的成员函数既可以在类的定义内也可以在类的定义外定义。(如上述代码中avg_price在类内声明,类 外定义。same_isbn在类内定义,编译器隐式的将类内定义的成员函数当作内联函数。---PS:内联函数 将在下一节中详细介绍。)
2、this指针的引入
bool same_isbn(const Sales_item &rhs)const
{ return isbn == rhs.isbn;}
上面的函数体就一句话:isbn == rhs.isbn,就是比较两个isbn,并返回比较结果。
分析:rhs.isbn肯定是传递给此函数实参的isbn成员,那么没有前缀的isbn的用法是指的哪一个?这个没有前缀 的isbn指的是用于调用函数的对象的isbn成员。(即:哪个对象调用isbn这个函数,那么这个isbn就是这 个对象的成员。还是比较抽象,我们会在下面一步步的剥离。)
原始问题的原始代码:
问题:假定isbn的所有交易出现在一起。程序把每个isbn的数据组合至命名为total的Sales_item对象中。从标准输入中读取每一笔交易将被存储到命名为trans的第二个Sales_item对象中。每读取一笔新的交易,就将它与total中Sales_item对象相比较,如果对象含有相同的isbn,就更新total;否则就输出total的值,并使用刚读入的交易重置total。(问题来源于C++ primer)
#include <iostream>
#include "Sales_item.h"
int main()
{
Sales_item total,trans;
if(std::cin>>total){
while (std::cin>>trans)
if(total.same_isbn(trans))
total=total+trans;
else{
std::cout<<total<<std::endl;
total=trans;
}
std::cout<<total<<std::endl;
}else{
std::cout<<"No data:"<<std::endl;
return -1;
}
return 0;
}
综合上面的没有前缀的isbn可以发现,没有前缀的isbn是指的哪个对象的成员,这地方是total对象调用same_isbn
这个成员,那么就是total这个对象与rhs的isbn进行比较,因为rhs是trans。所以这地方就很明确了。
结论:每个成员函数都有一个额外的、隐含的形参将该成员函数与调用该函数的类对象捆绑在一起,当调用名为total
的对象的same_isbn时,这个对象也传递给了函数。而same_isbn函数使用isbn时,就隐式的使用了该函数对象的
isbn成员。其实就是比较total.isbn与trans.isbn两个值。
问题:那么这个隐含的形参是如何将成员函数与调用该函数类对象捆绑在一起的呢?
分析:每个成员函数都有一个额外的、隐含的形参this。在调用成员函数时,形参this初始化为调用函数的对象的
地址。(其实就把this初始化为调用这个函数的对象的地址,上面的就是total的地址)。
对于total.same_isbn(trans);编译器就要重写成:
Sales_item::same_isbn(&total,trans);
在这个调用中,函数same_isbn中的数据成员isbn属于对象total
---------------------------------------------------分割线--------------------------------------------
现在就来说说const成员函数的引入问题了:bool same_isbn(const Sales_item &rhs) const
问题:跟在跟在成员函数声明的形参后面的const起到什么作用?
const改变了隐含的this形参的类型。在调用total.same_isbn(trans)时,隐含的this形参将式一个指向total对象的const Sales_item *类型的指针,就可以像如下编写same_isbn的函数体一样:
bool Sales_item::same_isbn(const Sales_item *const this,const Sales_item &rhs) const
{
return (this->isbn == rhs.isbn);
}
用这种方式使用const的函数称之为常量成员函数。
总结:上面从成员函数函数体的总结到引入const成员函数,尤其里面提到一个this指针的问题,看似比较复杂,但是实际理解起来还是比较简单的。
我们总结一下看看:
当我们调用某个成员函数时,此时要对两个不同的对象的某个成员进行比较时,我们可以简单的写成”成员 == 某个对象.成员“这样的模式,我们其实是不知道没有前缀的成员到底是谁的成员。这里面的成员函数就为我们提供了一个额外的、隐含的形参。这个形参就是this指针,当某个对象调用成员函数时,形参this就被初始化为调用函数的这个对象的地址,这样,即使没有前缀,也能很好的指向了。而跟在成员函数的形参后面的const所起到的作用就是改变这个隐含的this形参类型。
这里面还有很多内容需要拓展:比如this指针如何使用的问题,内联函数是什么的问题,这就放在下一节中来总结。