面向对象中关于继承的总结。

#@author:       gr
#@date: 2015-07-26
#@email: forgerui@gmail.com

一、类的隐藏

重载(overload)、覆盖(override)与隐藏(hidden)。

重载:

相同的范围(在同一个类中)

函数名字相同

参数不同

virtual 关键字可有可无

覆盖(重写):

不同的范围(分别位于派生类与基类)

函数名字相同

参数相同

基类函数必须有 virtual 关键字

隐藏:

如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载混淆)

如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有 virtual关键字。此时,基类的函数被隐藏,基类有virtual关键字的话就是覆盖了。

父类是virtual方法          形参表相同  ---> 构成重写
父类是virtual方法 形参表不同 ---> 隐藏父类方法
父类不是virtual方法 形参表相同 --->隐藏父类方法
父类不是virtual方法 形参表不同 --->隐藏父类方法

  

总结:

重载肯定是在一个域中的,C++不支持跨域重载,即子类同名函数永远不与父类函数形成重载,而是隐藏掉父类同名函数。

覆盖的要求比较严格,子类与父类只有一种情况构成重写,三种情况下子类与父类同名函数构成隐藏。

继承类函数会隐藏子类中所有同名的函数,即使是子类中的虚函数也会隐藏所有同名函数,即使将父类的函数声明为virtual都不会改善这种情况。

class A {
public:
void print() { //加上virtual不会改善这种情况
cout << "hello base class" << endl;
}
}; class B : public A {
public:
void print(int) { //加上virtual也不会改善这种情况
cout << "hello inheritance class" << endl;
}
};

上面的print(int)会隐藏掉所有基类中叫print的函数。

B b;
b.print(); //报错,子类的该函数已被隐藏

如果想要调用A中的函数,需要使用类作用域来限定:

B b;
b.A::print(); //类作用域调用

除此之外,还可以在子类中使用using声明基类函数,如下:

class B : public A {
public:
using A::print; //注意这里没有括号
void print(int) {
cout << "hello inheritance class" << endl;
}
};

二、多态实现

普通函数表现在静态绑定上,即根据当前类类型决定调用函数,而不是实际的对象类型。如下:

class A {
public:
void print() {
cout << "hello base class" << endl;
}
}; class B : public A {
public:
void print() {
cout << "hello inheritance class" << endl;
}
}; void main() {
A* pA = new B();
pA->print(); //此句会调用A::print()
}

上面pA只会调用A类的print()函数,如果我们想根据实际对象的类型来调用相应的函数,那么就需要进行动态绑定。将基类的print()函数声明为virtual,可以解决这个问题。

class A {
public:
virtual void print() {
cout << "hello base class" << endl;
}
};
class B : public A {
public:
void print() {
cout << "hello inheritance class" << endl;
}
}; void main() {
A* pA = new B();
pA->print(); //根据对象的实际类型调用B::print()
}

虚函数的成本:

当然,"天下没有免费的午餐"。实现这种功能必须花费一定的成本。

一般函数是不会增加对象空间的,一般C++对象的大小由非静态成员变量,虚函数,以及对齐决定。

而使用虚函数,主要有两个成本

  1. 一个类会在内存中建立一个虚函数表(vtbl),用来记录虚函数信息,所有该类对象共享该虚函数表。类的虚函数表是一块连续的内存,每个内存单元中记录一个JMP指令的地址,也就是每个函数的地址。
  2. 其次,会在每个类对象内增加一个指针(vptr, 32位的话占用4个字节),用以指向虚函数表。

因为基类、子类有两个不同的虚函数表,且两个虚函数指针指向各自的虚函数表,所以在运行时可以体现出多态。同理,如果作为函数参数,不能以值传递,这样对象会被slice,必须传递指针或引用。虚函数需要在运行时才能确定运行的函数,但在编译时就要给出可以执行的命令,主要是如下几步:

  1. 根据pC1所指对象的虚函数指针(vptr)索引到虚函数表(vtbl)。
  2. 在虚函数表中找到函数的偏移,记为i。
  3. 调用第二步中的函数,*(pC1->vptr[i])(pC1);

三、类的权限控制

  1. 继承方式

    public继承: public => public, protect =>protect, private => 不可访问

    protect继承:public => protect, protect =>protect, private => 不可访问

    private继承:public => private, protect => private, private => 不可访问

  2. 子类成员

    public成员:public => public, protect => protect, private => private

    protect成员:public => protect, protect => protect, private => private

    private成员:都不可访问

默认继承方式是private继承(所以如果需要使用子类成员,需要public继承),默认成员是private。

四、虚基类

含有纯虚函数的类叫做虚基类,类似Java里的接口(interface)。虚基类无法被实例化,只能通过继承,实现子类。

class AbstractBase {
public:
virtual void solve() = 0; //纯虚函数
virtual ~AbstractBase(){}
};
class Derivation : public class AbstractBase {
public:
void solve(){}
}; int main() {
AbstractBase* thing = new Derivation();
thing->solve();
}

五、继承与组合

继承是is a关系,组合是has a关系。有时需要根据事实正确塑模出其关系。

六、多继承与虚继承

为了满足一个类包含两个类的性质,C++提供多继承。

为了解决多继承带来的包含多个基类问题,使用虚继承,可以只包含一个基类。

class A {};
class B1 : virtual public A {}; //虚继承
class B2 : virtual public A {}; //虚继承
class C : public B1, public B2 {}; //使用虚继承,C中只包含一个A

### C++总结-[类的继承]的更多相关文章

  1. UML类图(上):类、继承和实现

    面向对象设计 对于一个程序员来说,在工作的开始阶段通常都是别人把东西设计好,你来做.伴随着个人的成长,这个过程将慢慢变成自己设计一部分功能来实现,自己实现.如果要自己设计,无论是给自己看,还是给别人看 ...

  2. 【Python五篇慢慢弹(5)】类的继承案例解析,python相关知识延伸

    类的继承案例解析,python相关知识延伸 作者:白宁超 2016年10月10日22:36:57 摘要:继<快速上手学python>一文之后,笔者又将python官方文档认真学习下.官方给 ...

  3. (转)Java:类与继承

    原文地址: http://www.cnblogs.com/dolphin0520/p/3803432.html 对于面向对象的程序设计语言来说,类毫无疑问是其最重要的基础.抽象.封装.继承.多态这四大 ...

  4. iBatis.net 类的继承extends和懒加载

    <resultMaps> <resultMap id="FullResultMap" class="t_c_team_member_permission ...

  5. python 类定义 继承

    0 前言 系统:win7 64bit IDE : python(x,y) 2.7.6.1 IDE集成的解释器:Python 2.7.6 (default, Nov 10 2013, 19:24:18) ...

  6. JS原型继承和类式继承

    前言 一个多月前,卤煮读了一篇翻译过来的外国人写的技术博客.此君在博客中将js中的类(构造)继承和原型继承做了一些比较,并且得出了结论:建议诸位在开发是用原型继承.文中提到了各种原型继承的优点,详细的 ...

  7. Java编程里类的继承

    今天,我们将要讨论的内容是Java里面类的继承的相关概念. 说到继承,我相信大家都不陌生.生活中,子承父业,子女继承父母的财产,这就是继承.实际上,Java里的继承也是如此.对于一个类来说,它的数据成 ...

  8. Java:类与继承

    Java:类与继承 对于面向对象的程序设计语言来说,类毫无疑问是其最重要的基础.抽象.封装.继承.多态这四大特性都离不开类,只有存在类,才能体现面向对象编程的特点,今天我们就来了解一些类与继承的相关知 ...

  9. swift_Class类的继承

    //: Playground - noun: a place where people can play var str = "Hello, playground" //***** ...

  10. C#类的继承,方法的重载和覆写

    在网易云课堂上看到唐大仕老师讲解的关于类的继承.方法的重载和覆写的一段代码,注释比较详细,在此记下以加深理解. 小总结: 1.类的继承:允许的实例化方式:Student t=new Student() ...

随机推荐

  1. 在VS中安装EF和项目引用EF

    1.通过Visual Studio安装NuGet (1). 打开Visual Studio扩展管理器     (2). 选择联机库,并在搜索中写入NuGet,然后点击搜索结果中NuGet Packag ...

  2. WinDbug之DUMP蓝屏分析

    Microsoft (R) Windows Debugger Version 6.2.8400.0 X86Copyright (c) Microsoft Corporation. All rights ...

  3. VS2015创建的Asp.net WebApi默认项目在CentOS7+Mono4.2.2+jexus5.8运行不起来的解决方案

    主要原因是Web.config配置的问题. 修改成如下内容: <?xml version="1.0" encoding="utf-8"?> < ...

  4. C# 手动读写app config 的源码

    public class ConfigOperator { public string strFileName; public string configName; public string con ...

  5. WPF基础到企业应用系列7——深入剖析依赖属性(WPF/Silverlight核心)

    一. 摘要 首先圣殿骑士非常高兴这个系列能得到大家的关注和支持.这个系列从七月份開始到如今才第七篇,上一篇公布是在8月2日,掐指一算有二十多天没有继续更新了,最主要原因一来是想把它写好,二来是由于近期 ...

  6. HDU 3410 &amp;&amp; POJ 3776 Passing the Message 单调队列

    题意: 给定n长的数组(下标从1-n)(n个人的身高,身高各不同样 问:对于第i个人,他能看到的左边最矮的人下标.(假设这个最矮的人被挡住了,则这个值为0) 还有右边最高的人下标,同理若被挡住了则这个 ...

  7. [IOS]自定义长触屏事件

    写一个Demo来自定义一个长触屏事件,自定义长按手势. 实现步骤: 1.创建一个自定义手势类,命名为LongPressGestureRecognizer,在创建的时候继承UIGestureRecogn ...

  8. android141 360 安装软件管理

                      主界面: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/andro ...

  9. android139 360 黑名单 增删改查-数据库操作

    BlackNumberOpenHelper.java package com.itheima52.mobilesafe.db.dao; import android.content.Context; ...

  10. 文本编辑器Nano实用快捷键

    一.复制.剪切和粘贴文本 1.行复制.剪切和粘贴 Alt+6:复制光标所在行. Ctrl+K:剪切光标所在行. Ctrl+U:粘贴. 2.自由复制.剪切和粘贴 自由复制: Ctrl+6:设置复制文本的 ...