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

#include<iostream>
#include<string>
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<<it.isbn<<"\tnumber sold: "<<n<<"\ttotal price: "<<it.net_price(n)<<endl;
}int main(){Item it1("201307291540",16.5);cout<<it1.net_price(2)<<endl;Bulk_Item it2("201307291545",22.5,10,0.2);cout<<it2.net_price(20)<<endl;print_total(it1,4);print_total(it2,20);return 0;
}

同学的疑问是,如果去掉 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<iostream>
#include<string>
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"<<endl;return price;};double get_price(){cout<<"get Non const"<<endl;return price;    };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<<it.isbn<<"\tnumber sold: "<<n<<"\ttotal price: "<<it.net_price(n)<<endl;
}int main(){Item it1("201307291540",16.5);cout<<it1.net_price(2)<<endl;Bulk_Item it2("201307291545",22.5,10,0.2);cout<<it2.net_price(20)<<endl;print_total(it1,4);print_total(it2,20);  cout<<it2.get_price()<<endl;return 0;
}

输出结果为:

get Non const
22.5

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

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

#include<iostream>
#include<string>
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"<<endl;return price;};double get_price(){cout<<"get Non const"<<endl;return price;    };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<<it.isbn<<"\tnumber sold: "<<n<<"\ttotal price: "<<it.net_price(n)<<endl;
}void print_price(const Bulk_Item &it){cout<<"print_price const"<<endl;cout<<it.get_price()<<endl;
}void print_price(Bulk_Item &it){cout<<"print_price Non const"<<endl;cout<<it.get_price()<<endl;
}int main(){Item it1("201307291540",16.5);cout<<it1.net_price(2)<<endl;Bulk_Item it2("201307291545",22.5,10,0.2);cout<<it2.net_price(20)<<endl;print_total(it1,4);print_total(it2,20);cout<<it2.get_price()<<endl; print_price(it2);return 0;
}

输出结果为:

print_price Non const
get Non const
22.5

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

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

#include<iostream>
#include<string>
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"<<endl;return price;};double get_price(){cout<<"get Non const"<<endl;return price;    };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<<it.isbn<<"\tnumber sold: "<<n<<"\ttotal price: "<<it.net_price(n)<<endl;
}void print_price(const Bulk_Item &it){cout<<"print_price const"<<endl;cout<<it.get_price()<<endl;
}void print_price(Bulk_Item &it){cout<<"print_price Non const"<<endl;cout<<it.get_price()<<endl;
}int main(){Item it1("201307291540",16.5);cout<<it1.net_price(2)<<endl;Bulk_Item it2("201307291545",22.5,10,0.2);cout<<it2.net_price(20)<<endl;print_total(it1,4);print_total(it2,20);cout<<it2.get_price()<<endl;print_price(it2);const Bulk_Item it3("201307291546",44,10,0.2);print_price(it3);return 0;
}

则会打印:

print_price const
get const
44

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

#include<iostream>
#include<string>
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"<<endl;return price;};double get_price(){cout<<"get Non const"<<endl;return price;    };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<<it.isbn<<"\tnumber sold: "<<n<<"\ttotal price: "<<it.net_price(n)<<endl;
}void print_price(const Bulk_Item &it){cout<<"print_price const"<<endl;cout<<it.get_price()<<endl;
}void print_price(Bulk_Item &it){cout<<"print_price Non const"<<endl;cout<<it.get_price()<<endl;
}int main(){Item it1("201307291540",16.5);cout<<it1.net_price(2)<<endl;Bulk_Item it2("201307291545",22.5,10,0.2);cout<<it2.net_price(20)<<endl;print_total(it1,4);print_total(it2,20);cout<<it2.get_price()<<endl;print_price(it2);const Bulk_Item it3("201307291546",44,10,0.2);cout<<it3.get_price()<<endl;print_price(it3);return 0;
}

则会打印:

get const
44

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

#include<iostream>
#include<string>
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<<it.isbn<<"\tnumber sold: "<<n<<"\ttotal price: "<<it.net_price(n)<<endl;
}int main(){Item it1("201307291540",16.5);cout<<it1.net_price(2)<<endl;Bulk_Item it2("201307291545",22.5,10,0.2);cout<<it2.net_price(20)<<endl;print_total(it1,4);print_total(it2,20);return 0;
}

最后一行输出为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

C++中const——由一个例子想到的相关推荐

  1. R语言使用lm构建线性回归模型、并将目标变量对数化(log10)实战:可视化模型预测输出与实际值对比图、可视化模型的残差、模型预测中系统误差的一个例子 、自定义函数计算R方指标和均方根误差RMSE

    R语言使用lm构建线性回归模型.并将目标变量对数化(log10)实战:可视化模型预测输出与实际值对比图.可视化模型的残差.模型预测中系统误差的一个例子 .自定义函数计算R方指标和均方根误差RMSE 目 ...

  2. Booth算法在局域网监控软件中运用的一个例子

    Booth算法是一种高效的二进制乘法算法,可用于在局域网监控软件中进行IP地址的匹配和查找.局域网监控软件通常需要对多台计算机进行监控和管理,而这些计算机的IP地址是关键的识别信息.使用Booth算法 ...

  3. python中self_一个例子带你入门Python装饰器

    ============ 欢迎关注我的公众号:早起python ============ 前言 在还未正式发布的python3.9中,有一个新功能值得关注,那就是任意表达式可以作为装饰器,如果你还不知 ...

  4. 无人机设计中的下一个进化步骤

    据一些人说,无人驾驶飞机(UAV)的商业意义,或无人驾驶飞机,因为他们更为人所知,可能和互联网一样重要.高调的应用如航空摄影和录像,这在很大程度上被民主化的无人机,有群众诉求和相应的高容量.更为深奥的 ...

  5. 一个例子来使用sklearn中的TfidfVectorizer

    TfidfVectorizer 作用 将文本进行向量化表示. 原理 这里的tf(term frequency)是词的频数,idf(inverse document frequency)是这个词的逆文档 ...

  6. Java黑皮书课后题第9章:9.2(Stock类)遵照9.2节中Cirlce类的例子,设计一个名为Stock的类

    Java黑皮书课后题第9章:9.2(Stock类)遵照9.2节中Cirlce类的例子,设计一个名为Stock的类 题目 代码 Test2 Test2_Stock UML图 题目 代码 Test2:测试 ...

  7. Java黑皮书课后题第9章:9.1 (Rectangle类)遵照9.2节中Circle类的例子,设计一个名为Rectangle的类表示矩形

    Java黑皮书课后题第9章:9.1 (Rectangle类)遵照9.2节中Circle类的例子,设计一个名为Rectangle的类表示矩形 题目 代码 PS Test1 Test1_Rectangle ...

  8. uniapp 获取到js文件var一个变量怎么获取到这个变量值_浅析Js中const,let,var的区别及作用域...

    理解:let变量的作用域只能在当前函数中 js中const,let,var的区别及作用域_lianzhang861的博客-CSDN博客​blog.csdn.net 全局作用域中,用 const 和 l ...

  9. 一个例子带你搞懂python作用域中的global、nonlocal和local

    在编程中,只要接触过函数的,我相信都理解什么是全局变量和局部变量,概念比较简单,这里就不做解释了.在python中,用global语句就能将变量定义为全局变量,但是最近又发现有个nonlocal,一时 ...

最新文章

  1. Annotation
  2. RepVGG:极简架构,SOTA性能,让VGG式模型再次伟大 | CVPR-2021
  3. #中regex的命名空间_Python命名空间实例解析
  4. 蓝桥杯java第六届决赛第四题--穿越雷区
  5. 【DirectX12龙书机翻整理】第8章 光照
  6. 使用Logstash filter grok过滤日志文件
  7. STL源码剖析---list
  8. 软件工程-东北师大站-第九次作业(PSP)
  9. java基础(六)多线程/设计模式
  10. CentOS系统中安装 vmware-tools
  11. 自定义值类型一定不要忘了重写Equals,否则性能和空间双双堪忧
  12. python的打开_记录
  13. python txt转dataframe_Python格式化解析不规则txt文本并转为dataframe
  14. 陈彤:一个网络编辑的11年
  15. android 自定义view 水波纹进度球
  16. Android开机画面显示
  17. Win10系统安装教程
  18. 永洪科技怎么样_【永洪科技怎么样?】-看准网
  19. 使鼠标保持按住状态_让电脑也能听、会写、还会说!小米小爱鼠标抢先体验
  20. 逆向记录Assaultcube for Mac OS

热门文章

  1. 利用反射机制获取未知类型的枚举的信息
  2. 【BZOJ 3171】 [Tjoi2013]循环格
  3. 在.net中如何禁用或启用DropDownList的Items
  4. PHP和Javascript实现转换文件大小为人性化可读方式
  5. 手工实现字节对齐 及 代码质量思考
  6. ASP.NET 2.0 – 善用DataSourceMode属性
  7. 测试新版FCKeditor编辑器精简版
  8. laravel实现多数据源配置和读写分离
  9. mvc在页面上显示PDF
  10. 一文彻底搞清 Gradle 依赖【转】