博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++中const——由一个例子想到的
阅读量:5984 次
发布时间:2019-06-20

本文共 7068 字,大约阅读时间需要 23 分钟。

前天同学实现了《C++ Primer》中关于虚函数的一个例子,拿过来问我,代码如下:

#include
#include
using namespace std;class Item{public: Item(const string x,const double y){isbn=x;price=y;}; virtual double net_price(size_t n) const{
return n*price;};//可以去掉virtual体验到第3、4行打印出来的区别 virtual ~Item(){}; string isbn;protected: double price;};class Bulk_Item:public Item{public: Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){}; double net_price(size_t n) const; //double get() const; //double get();private: size_t min_qty; double discount;};double Bulk_Item::net_price(size_t n) const{ if(n>=min_qty) return n*(1-discount)*price; else return n*price;}void print_total(const Item &it,size_t n){ cout<
<<"\tnumber sold: "<
<<"\ttotal price: "<
<

 

 同学的疑问是,如果去掉 Item 类定义中

virtual double net_price(size_t n) const{
return n*price;};

 

一句中的 const 限定,则会在print_total 的定义行出现编译错误

“Item::net_price”: 不能将“this”指针从“const Item”转换为“Item &”

他觉得定义的 net_price 方法并没有修改 Item 内的属性,有没有const限定词应该是一样的。 

但实际上,在print_total 的定义中,限定了引用 it 为 const, 这意味着该引用只能调用 Item 类内的 const方法,即:const限定的对象引用不能调用此对象中的非const方法。

进而,可以知道,C++中判断一个方法是不是const,并不是检测你在该方法中有没有改变类的属性,而只是简单的看你有没有const限定词,有就是const方法,没有就不是const方法。若有const限定词,而在方法内你又试图改变类的属性,则编译器会报错。

 

那么,同名函数(包括参数相同),一个有const限定,一个没有,是两个函数还是一个函数?为了探究,在 Bulk_Item 的定义中增加两个 get_price 函数,一个为const,一个为普通函数,然后在主函数里调用:

#include
#include
using namespace std;class Item{public: Item(const string x,const double y){isbn=x;price=y;}; virtual double net_price(size_t n) const{
return n*price;};//可以去掉virtual体验到第3、4行打印出来的区别 virtual ~Item(){}; string isbn;protected: double price;};class Bulk_Item:public Item{public: Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){}; double net_price(size_t n) const; double get_price() const{ cout<<"get const"<
=min_qty) return n*(1-discount)*price; else return n*price;}void print_total(const Item &it,size_t n){ cout<
<<"\tnumber sold: "<
<<"\ttotal price: "<
<

 

输出结果为:

get Non const22.5

可见,并没有产生编译错误,自动调用的是非const方法。故有无const会造成有两个重载的函数,编译后的函数名,除了函数名和参数,还加入了const限定词。但调用时会优先选非const的函数(这是因为我们传入的是非const对象it2,下面会看到,若传入const对象,则自动调用const版本)。

接着,我们构造打印函数,如下:

#include
#include
using namespace std;class Item{public: Item(const string x,const double y){isbn=x;price=y;}; virtual double net_price(size_t n) const{
return n*price;};//可以去掉virtual体验到第3、4行打印出来的区别 virtual ~Item(){}; string isbn;protected: double price;};class Bulk_Item:public Item{public: Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){}; double net_price(size_t n) const; double get_price() const{ cout<<"get const"<
=min_qty) return n*(1-discount)*price; else return n*price;}void print_total(const Item &it,size_t n){ cout<
<<"\tnumber sold: "<
<<"\ttotal price: "<
<

 

输出结果为:

print_price Non constget Non const22.5

 

可见,(1)调用时,优先调用参数为非const的函数(因为传入的it2是非const参数),编译器不会在你传入非const参数时调用const参数的函数(除非没有非const版本),这是合理的,否则你不能改变你想改变的;(2)函数内,自动调用非const版本get_price(因为传入的是非const引用,故优先调用get_price方法的非const版本)。

同理,如果传入const 参数,如下:

#include
#include
using namespace std;class Item{public: Item(const string x,const double y){isbn=x;price=y;}; virtual double net_price(size_t n) const{
return n*price;};//可以去掉virtual体验到第3、4行打印出来的区别 virtual ~Item(){}; string isbn;protected: double price;};class Bulk_Item:public Item{public: Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){}; double net_price(size_t n) const; double get_price() const{ cout<<"get const"<
=min_qty) return n*(1-discount)*price; else return n*price;}void print_total(const Item &it,size_t n){ cout<
<<"\tnumber sold: "<
<<"\ttotal price: "<
<

 

则会打印:

print_price constget const44

 

说明,(1)传入const参数会调用参数为const的函数,这是理所应当的;(2)在print_price里会调用const版本的get_price,这说明,如果我们对一个类,有同名的两个函数,一个为const,一个非const,若用一个const对象引用来调用这个同名函数,则自动调用那个const函数。如下所示:

#include
#include
using namespace std;class Item{public: Item(const string x,const double y){isbn=x;price=y;}; virtual double net_price(size_t n) const{
return n*price;};//可以去掉virtual体验到第3、4行打印出来的区别 virtual ~Item(){}; string isbn;protected: double price;};class Bulk_Item:public Item{public: Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){}; double net_price(size_t n) const; double get_price() const{ cout<<"get const"<
=min_qty) return n*(1-discount)*price; else return n*price;}void print_total(const Item &it,size_t n){ cout<
<<"\tnumber sold: "<
<<"\ttotal price: "<
<

 则会打印:

get const44

 故而,鉴于有无const限定词会造就两个不同的函数,所以如果基类中有const,而继承类中同名方法没有const,则其实继承类实现的是一个完全新的函数,而不是在覆盖基类的方法。向上类型转换时,会调用基类的方法,而不是继承类中同名的非const方法,如下:

#include
#include
using namespace std;class Item{public: Item(const string x,const double y){isbn=x;price=y;}; virtual double net_price(size_t n) const{
return n*price;};//可以去掉virtual体验到第3、4行打印出来的区别 virtual ~Item(){}; string isbn;protected: double price;};class Bulk_Item:public Item{public: Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){}; double net_price(size_t n) ;private: size_t min_qty; double discount;};double Bulk_Item::net_price(size_t n) { if(n>=min_qty) return n*(1-discount)*price; else return n*price;}void print_total(const Item &it,size_t n){ cout<
<<"\tnumber sold: "<
<<"\ttotal price: "<
<

最后一行输出为450而不是360。

同理,若基类方法无const,而继承类中有,则向上类型转换还是会调用基类的方法。

总结一下,主要有这么几点:

1、const限定的对象引用不能调用此对象中的非const方法。

2、const关键词限定的方法,则编译器就认为它是const型的,即使它并没有改变对象的任何属性。若限定了const,又在方法内改变了属性,则编译器会报错。

3、同名函数(参数也相同),一个有const限定,一个没有,编译器认为是两个重载函数,不会报错。这就意味着C++中编译器生成函数名时,除了函数名、参数外还有const,由三者共同组成编译后的函数名。

4、如上,两个方法的方法名和参数完全相同,一个有const限定,一个没有,则若你用const对象引用调用,会自动调用const版本的方法;若用非const对象引用调用,则会自动调用非const的版本。

5、同样的函数,只是参数一个是const,一个非const,则你用const参数调用,会自动调用参数为const的版本;你用普通参数调用,会自动调用参数为普通参数的版本,若此时没有非const的版本,它才会转而调用const版本的。

6、如果基类中有const,而继承类中同名方法没有const,则其实继承类实现的是一个完全新的函数,而不是在覆盖基类的方法,向上类型转换时,会调用基类的方法,而不是继承类中同名的非const方法。

突然想到,学习一门语言的最好方式也就就是:写一个这个语言的编译器。这样你就能对它的限制,它能做什么和不能做什么,你能用它做什么和不能做什么,有比较深刻的理解和记忆。推而广之,人类语言有很多冗余性,语法限制也相对比较宽松,故人类语言的编译器应该很难写。若能写一个人类语言的编译器,那就是真正的自然语言理解。

转载于:https://www.cnblogs.com/rolling-stone/p/3223454.html

你可能感兴趣的文章
【6】连续序列和为s
查看>>
Unity多媒体展示项目经验分享-ImageEffect+动态绑定
查看>>
Ext.Net常用方法
查看>>
Linux perf tools
查看>>
Java final自变量
查看>>
.Net批量插入数据到SQLServer数据库,System.Data.SqlClient.SqlBulkCopy类批量插入大数据到数据库...
查看>>
[AngularJS Ng-redux] Integrate ngRedux
查看>>
[CentOs7]安装mysql(2)
查看>>
OpenCV——级联分类器(CascadeClassifier)
查看>>
mysql主从复制 转
查看>>
[原创] WINDOWS 7 精简教程之驱动精简 可用于64和32
查看>>
Data Science 和 Finance 两个领域的融合是什么样子的?
查看>>
Java经典实例:把字符串解析为日期时间
查看>>
动态语言和静态语言的比较(转)
查看>>
音乐播放时跳动的音符
查看>>
【转载】爷爷和我---来自泊小豆的微博
查看>>
[转]关于信息安全认证CISP与CISSP的对比及分析
查看>>
自编段子
查看>>
K-mean和k-mean++
查看>>
神经网络架构整理
查看>>