C++中的重载、覆盖、隐藏介绍


前几天面试时被问及C++中的覆盖、隐藏,概念基本答不上来,只答了怎么用指针实现多态,也还有遗漏。最终不欢而散。回来后在网上查找学习了一番,做了这个总结。其中部分文字借用了别人的博客,望不要见怪。

•概念

一、重载(overload)
指函数名相同,但是它的参数表列个数或顺序,类型不同。但是不能靠返回类型来判断。
(1)相同的范围(在同一个作用域中) ;
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
(5)返回值可以不同;

二、重写(也称为覆盖 override)
是指派生类重新定义基类的虚函数,特征是:
(1)不在同一个作用域(分别位于派生类与基类) ;
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有 virtual 关键字,不能有 static 。
(5)返回值相同(或是协变),否则报错;<—-协变这个概念我也是第一次才知道…

(6)重写函数的访问修饰符可以不同。尽管 virtual 是 private 的,派生类中重写改写为 public,protected 也是可以的

三、重定义(也成隐藏)
(1)不在同一个作用域(分别位于派生类与基类) ;
(2)函数名字相同;
(3)返回值可以不同;
(4)参数不同。此时,不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载以及覆盖混淆) 。
(5)参数相同,但是基类函数没有 virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆) 。

•例子

#include <iostream>
using namespace std;
class SParent
{
public:
  SParent( ){};
  SParent( const SParent &p )
  {
    cout << "parent copy construct" << endl;
  }
  int add( int a,int b )
  {
    cout << "parent int add" << endl;
    return a + b;
  }
  double add( double a,double b )
  {
    cout << "parent double add" << endl;
    return a + b;
  }
  virtual int dec( int a,int b )
  {
    cout << "parent int dec" << endl;
    return a - b;
  }
};
class SChild : public SParent
{
public:
  //using SParent::add;
  float add( float a,float b )
  {
    cout << "child float add" << endl;
    return a + b;
  }
  int dec(int a, int b)
  {
    cout << "child int dec" << endl;
    return a - b;
  }
};
int main()
{
  /* 测试重载 */
  SParent parent;
  parent.add( 3,5 );
  parent.add( (double)3,(double)5 );
  cout << endl;
  /* 测试覆盖 */
  SChild *pchild = (SChild *)new SParent();/* 基类强转为子类...危险...,用dynamic_cast转换也不行 */
  pchild->dec( 10,3 );
  SParent *pparent = new SChild();
  pparent->dec( 11,3 );
  cout << endl;
  /* 测试隐藏 */
  SChild child;
  child.add( (int)3,(int)5 );
  cout << endl;
  /* 测试函数表 */
  ((SParent *)NULL)->add( 4,6 );
  ((SChild *)NULL)->add( 4,6 );
  int a = 0;
  ((SChild *)&a)->add( 4,6 );
   cout << endl;
  /* 测试函数地址 */
  ((SParent)child).add( (int)4,(int)8 );
  child.SParent::add( 3,5 );

  return 0;
}

输出结果:

parent int add
parent double add

parent int dec
child int dec

child float add

parent int add
child float add
child float add

parent copy construct
parent int add
parent int add
按 <RETURN> 来关闭窗口...

•理解
int SParent::add(int a,int b)与double SParent::add( double a,double b )是重载

int SParent::add(int a,int b)与double SParent::add( double a,double b )都被子类SChild中的float SChild::add( float a,float b )隐藏

int SParent::dec( int a,int b )被子类SChild中的int SChild::dec( int a,int b )覆盖

•测试

1.重载测试,简单易懂,略过。
2.覆盖测试。dec函数在基类、子类中同名同参,为虚函数,故称覆盖。

SChild *pchild = (SChild *)new SParent()创建的是一个基类对象,其函数表应该为

SParent *pparent = new SChild();创建一个子类对象,其函数表应该为

由上面的函数表可见,当发生覆盖时,子类的函数名会把基类的同名函数覆盖(这也就是为什么叫覆盖的原因吧)。这样我们可以利用一个指向子类的基类指针实现多态。但重点只有一
个,就是函数表里到底指向谁(不管这个指针经过转换后是什么类型的).故输出分别为父类、子类。这是一个运行时多态。

3.隐藏测试

int SParent::add(int a,int b)与double SParent::add( double a,double b )都被子类SChild中的float SChild::add( float a,float b )覆盖,是因为他们同名,而且在不同的作用域中(基类、子类作用域是不同的)。child.add( (int)3,(int)5 );这行代码中,编译器在子类中查找add函数,只找到了一个(基类的add(int a,int b)会被编译根据隐藏规则略过),再根据隐式类型转换发现该函数适用。如果无法隐式转换,则编译不过。隐藏的原因:防止隐式类型转换造成错误。比如int也是可以转换成char的,假如基类有一函数add(char a,char b),子类也有一函数add(double a,double b)。程序员想着在子类隐式把int转换为double,但编译器可能调的是基类的。这也防止了一些库或封装好的基类对程序员造成困扰。

  像上面的代码,如果你确实需要基类的函数,可以用using SParent:add。则把基类的add函数域扩大到了子类,构成重载。

4.函数表测试

上面我们说到函数表,这个是在编译时定好的,程序运行时加载到内存中。这意味着我们可以直接通过地址去调用函数。所以((SChild *)NULL)->add( 4,6 );这种代码也是能运行通过的。网上还有人通过计算直接取到了函数表的地址直接调用了。但这种代码不安全不规范不说,还有个更大的问题。当成员函数里需要调用成员变量时,通过这种假的对象指针肯定找不到成员变量表,直接访问了非法内存。

5.函数地址测试

有了隐藏、覆盖,哪么我们要怎么调用被隐藏、覆盖的函数呢。下面有两种方法:

    ((SParent)child).add( (int)4,(int)8 );
    child.SParent::add( 3,5 );

第一种是比较低效的方法。事实上它是通过拷贝构造函数生成一个临时的基类变量去调用基类的add函数。
第二种通过::指定域去访问。这种方法是编译器根据域直接找到了基类的函数地址,跟函数表没有多大关系。



相关阅读:
(图)全新windows 10 Build 10074泄露
node.js中的fs.fsyncSync方法使用说明
PHP无限分类(树形类)
浅析Java的Spring框架中IOC容器容器的应用
史上最详细的js日期正则表达式分享
试用php中oci8扩展
c#网络唤醒功能实现
jQuery ajax方法传递中文时出现中文乱码的解决方法
win8.1系统Chrome浏览器出现"没有注册类"的解决方法
js返回前一页刷新本页重载页面
C语言fillpoly函数详解
进行多文件内容导入数据库Linux如何将多个文件内容合成一个
在mac中怎么看文件夹大小 苹果mac查看文件夹大小图文教程
教你如何静默安装ORACLE
快速导航
PHP MySQL HTML CSS JavaScript MSSQL AJAX .NET JSP Linux Mac ASP 服务器 SQL jQuery C# C++ java Android IOS oracle MongoDB SQLite wamp 交通频道 作文范文 永康-明光 赵城-包头 玉门-开原 义乌-九江 沂水-贵阳 景泰-乌海西 麻尾-廉江 汤逊湖-咸宁东 蔡家坡-漯河 长兴-三门峡南 青岛北-枣庄 庐江-唐河 东安东-常州 大田边-石林 威箐-盘关 甘旗卡-大安北 商丘-襄垣 鞍山-北安 枣阳-天水 宁安-柴河 一面坡-羊草 昌图-完工 永甸-铁佛寺 平凉-礼泉 榆次-长沙 凯里南-盘州 北京-枣庄西 沈阳北-延吉西 铁口-乐跃 芜湖-石家庄 济南-三明北 德州-泗阳 扶余-东光 杭州东-茶陵南 缙云西-嘉善南 西平-北京西 株洲-邓州 宣化-静海 德清-红安西 横江-彝良 尚志-平原 于都-绍兴北 乐清-徐州东 古田北-潍坊 临汾-民权 道清-菇园 玉山-凯里 余姚北-醴陵东 保家楼-秀山 泰安-信阳东 三江南-三水南 麻城-诸城 高密-陶赖昭 仙游-惠东 高碑店-庐山 平顶山西-娄底 于都-彭水 开原西-济南 忻州-北京西 英德-怀化 石河子-砀山 嘉峪关-宝鸡 潢川-呼和浩特 阜南-惠州 沧州西-霸州西 茂名-天津 广州东-广州北 营山-鹰潭 保定-芦台 中宁-柴沟堡 山城镇-柳河 老莱-翠峰 玉山-应城 铁岭-阳平关 湘乡-阳谷 娄底-高密 延安-南岔 清水-广元 绥德-内乡 息烽-都匀 四岁的孩子在列车火车上发烧了,又没有药可以吃,该怎么办呀急呀
广东肇庆快递特产沙糖桔到江苏苏州要多少小时才到,还有要多
广州到三亚火车票价多少
仙剑三出去九顶山的理想等级是几级?
从上海南站到格林豪泰长阳店怎么坐车,急
南昌民德路时代广场写字楼怎么坐车
北京现代 越野 样式的汽车客车多少钱 能买下 要经济一点的
我是江西九江人,在抚州读书户口也迁到了学校,现在在浙江工
济南到烟台的。火车列车k8291怎么样软座和卧铺怎么样呢是红皮
平顶山到开封自行车路线
从东莞怎么去恩平市东成镇横岗头管区十字路侏罗纪工业园雷龙
有12:30到1:30从哈尔滨到肇东的列车吗
九江到景德镇铁路会开通动车吗
1801次火车066和065是靠窗户的吗
重庆到苏州有直达列车吗多少钱
赤峰到银川在北京倒车
如何从泰州去福州
九江哪里有卖汽车客车用品的
请问广州市到怀集县的大岗镇,自驾车走什么高速去,大约几公
8月8日的列车票几号出票
2010年11月拉萨到成都的火车列车T24是单号还是双号
请问有从长沙到安徽安安庆的长途客车吗
从武汉到苏州最便宜的列车火车票价多少坐到上海的列车火车中途到苏州
请问7月7号天津至哈尔滨的列车火车车次是多少
苏州太湖湿地公园,苏州太湖公园怎么走?是不是同一个公园?
广州大学到广州城市职业学院滨江校区(原广州业余大学)怎乘交
成都到雅安有多少公里,坐车要多少小时
我是上海人,到哈尔滨生活要注意些什么比如说带什么衣物,
请问从大水坑工业区怎么坐车到火车站?远吗?从深圳坐哪
乌鲁木齐到北京坐火车列车临客需要几天才能到达

Copyright © 2016 phpStudy |