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

#@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. Node.js和mybatis分别实现mysql中like变量模糊查询

    <!-- mybatis --> <where> <if test="varName != '' and varName != null" > ...

  2. [1.1]Environment preset on a Windows server

    1. Python 3.5.1 (also on your personal computer) 2. Django 1.10.1 (also on your personal computer) 3 ...

  3. 【M15】了解异常处理(exception handling)的成本

    1.为了在运行期处理异常,程序必须做大量额外的工作.比如,即使抛出异常,也必须保证离开作用域的栈上对象执行析构方法.因此,必须记录try语句的进入点和离开点,记录catch语句能够处理的异常等.这就意 ...

  4. Educational Codeforces Round 1 E. Chocolate Bar 记忆化搜索

    E. Chocolate Bar Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/598/prob ...

  5. C#中使用GUID的笔记

    GUID(全局统一标识符)是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的.通常平台会提供生成GUID的API.生成算法很有意思,用到了以太网卡地址.纳秒级时间.芯片ID码和许多可 ...

  6. C 高级编程 2 内存管理

    理解malloc的工作原理: malloc使用一个数据结构(链表)来维护分配的空间.链表的构成: 分配的空间.上一个空间的地址.下一个空间的地址.以及本空间的信息等. 对malloc分配的空间不要越界 ...

  7. 机器学习中的范数规则化之(一)L0、L1与L2范数 非常好,必看

    机器学习中的范数规则化之(一)L0.L1与L2范数 zouxy09@qq.com http://blog.csdn.net/zouxy09 今天我们聊聊机器学习中出现的非常频繁的问题:过拟合与规则化. ...

  8. mysql导入数据库

     mysql -u root -p bbs < d:\bbs_2011-06-15 --default-character-set=gbk      mysqldump -uroot -p ta ...

  9. 杂乱无章之javascript(二)

    1.浏览器与事件事件通常是由浏览器所产生,不同的浏览器会产生的事件也有所不同,即使同一浏览器不同版本所产生的事件也有不同.以下为HTML4.01中的事件 2.error事件:它可以调用一个错误处理函数 ...

  10. ArcGIS: version not specified. You must call RuntimeManager.Bind before creat

    打开program.cs把ESRI.ArcGIS.RuntimeManager.Bind(ESRI.ArcGIS.ProductCode.EngineOrDesktop);这句放到Applicatio ...