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

#@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. php+gd库的源码安装

    php+gd库的源码安装     PHP+GD安装   一.下载软件 gd-2.0.35.tar.gz          http://www.boutell.com/gd/ jpegsrc.v6b. ...

  2. IOC运用到MVC中

    IOC可以摒弃掉类中类的紧耦合,让设计和重用更简单,将IOC加入到MVC中的实现非常简单,那么有哪几种方法?它们的实现又是什么原理呢? IOC在MVC中的注入,主要是在获取Controller对象中实 ...

  3. 解决使用DevExpress开发错误:未将对象引用设置到对象的实例

    在使用DevExpress是总是会出现一些状况.这次同事在他的机器上调试完成的代码发过来,却出现"未将对象引用设置到对象的实例"的错误,提示是Resources.resx的问题.另 ...

  4. Oracle学习(七):集合运算

    1.知识点:能够对比以下的录屏进行阅读 SQL> -- 查询10和20号部门的员工的3种方法 SQL> --1. select * from emp where deptno in (10 ...

  5. ORACLE触发器具体解释

    ORACLE PL/SQL编程之八: 把触发器说透 本篇主要内容例如以下: 8.1 触发器类型 8.1.1 DML触发器 8.1.2 替代触发器 8.1.3 系统触发器 8.2 创建触发器 8.2.1 ...

  6. extern的困惑

    摘自:http://blog.csdn.net/fxjtoday/article/details/6021845 如果想明白为什么需要extern, 需要从编译和链接讨论起, 现代编译器一般采用按文件 ...

  7. Samba服务器配置参考链接

    一步一学Linux与Windows共享文件Samba(很适合初学者,极力推荐): http://os.51cto.com/art/200709/56395.htm 由最简单的一个例子说起,匿名用户可读 ...

  8. Steps to disable DRLs with GM Tech2 scanner

    It is possible to get daytime running time disabled manually. But the problem can be easily settled ...

  9. this的分析

    this是js中的一个关键字,当函数运行时,会自动生成的一个对象,只能在函数内部使用.随着函使用场合不同,this值会变化,但是始终指向调用函数的那个对象. 1.纯粹的函数调用 function bo ...

  10. 安装openshift客户端工具 rhc

    安装ruby: $ sudo apt-get install ruby-full 正在读取软件包列表... 完成 正在分析软件包的依赖关系树 正在读取状态信息... 完成 将会安装下列额外的软件包: ...