在C++继承模型中,一个派生类对象表现出来的东西,是其自己的成员加上其基类成员的总和。但这些成员怎样摆放,标准并未强制规定。一般而言,低地址放基类子对象,高地址放派生类对象。

以下从四个部分讨论C++继承模型:
  • 单一继承不含虚函数
  • 单一继承并含虚函数
  • 多重继承
  • 虚拟继承
1、单一继承不含虚函数

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbmVzdGxlcg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

这样的继承关系非常easy,基类子对象包括在了派生类对象中。在内存中连续存放。但有一点须要注意,把类分解成多层可能会造成空间的膨胀。比如:
#include <iostream>
#include <vector> using namespace std; class Foo {
public:
int val;
char bit1, bit2, bit3;
}; class A {
public:
int val;
char bit1;
}; class B : public A {
public:
char bit2;
}; class C : public B {
public:
char bit3;
}; int main()
{
cout << "size Foo = " << sizeof(Foo) << endl;
cout << "size C = " << sizeof(C) << endl;
system("pause");
return 0;
}
执行结果:
两个类中包括同样的成员。空间却差了一倍。这是由于基类须要边界对齐的缘故。C++语言保证,出如今派生类中的基类子对象有其完整原样性。这是关键所在。为什么要使用这样牺牲空间的布局?原因是在对象之间拷贝时。仅仅对子对象进行成员拷贝而不影响派生类中的成员。

2、单一继承并含虚函数
基类中有虚函数。那么编译器会给基类生成一个virtual function table和一个vptr。派生类会继承此vptr,但不会指向同样的virtual function table,而是指向自己的virtual function table。毕竟派生类一般都会重写从基类继承的虚函数。

关于vptr的摆放位置,要视编译器而定,要么放在对象开头,要么放在对象结尾。

我手头的VS2013就把vptr放在了对象的开头处。

能够看到,派生类Point3d中的vptr属于Point2d子对象。所以当一个基类指针指向派生类时,能够顺利取得这个vptr然后调用所需的虚函数以表现多态性。
以下做个实验:
#include <iostream>
#include <vector> using namespace std; class Foo {
public:
int x;
}; class Bar : public Foo {
public:
int y;
virtual void func()
{}
}; int main()
{
Bar bar;
cout << &bar << endl;
cout << &bar.x << endl;
cout << &bar.y << endl;
system("pause");
return 0;
}
执行结果:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbmVzdGxlcg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

Foo类没有虚函数。也就没有vptr。而派生类Bar有虚函数,编译器把它的vptr插在了类的开头处,先于基类成员摆放。
3、多重继承
对一个多重派生对象,将其地址指定给派生列表“最左端”的基类指针,情况将和单一继承时同样。由于二者都指向同样的起始地址。

对后继的基类指针的赋值,须要由编译器负责加上一个偏移地址。

比如有例如以下继承结构:
它的数据分布有可能例如以下:

如果有例如以下定义:
Vertex3d v3d;
Vertex *pv;
Point2d *p2d;
Point3d *p3d;
操作例如以下:
pv = &v3d;
p2d = &v3d;
p3d = &v3d;
对第一种赋值操作,编译器会产生相似以下的伪代码:
pv = (Vertex *)(((char *)&v3d) + sizeof(Point3d));
由于从布局图能够看出,指向Vertex的指针须要跳过开头的Point3d部分才干指向数据自己的子对象。然后。对第二和第三种赋值操作,仅仅须要简单地拷贝其地址就好,由于Point2d指针和Point3d指针都指向对象v3d的起始地址。
4、虚拟继承
由于虚拟基类是共享的,所以在各个派生类中必须要由编译器加入某种信息,用来保存共享的虚拟基类的地址。

关于怎样加入,各个编译器厂家的实现都有所不同,并且在未来也会有更新,这里就不详细说明了。

除了通过对象来存取虚基类中的成员之外。通过指针和引用都会引起执行成本上的额外开销。一般而言。虚基类最有效的一种运用形式就是:一个抽象的虚基类,没有不论什么数据成员。

參考:
《深度探索C++对象模型》 P99-P123.

C++继承模型的更多相关文章

  1. Entity Framework 6 Recipes 2nd Edition(10-6)译 -> TPT继承模型中使用存储过程

    10-6. TPT继承模型中使用存储过程 问题 想在一个TPT继承模型中使用存储过程 解决方案 假设已有如Figure 10-6所示模型. 在模型里, Magazine(杂志) and DVD继承于基 ...

  2. Entity Framework 6 Recipes 2nd Edition(10-7)译 -> TPH继承模型中使用存储过程

    10-7. TPH继承模型中使用存储过程 问题 用一个存储过程来填充TPH继承模型的实体 解决方案 假设已有如Figure 10-7所示模型. 我们有两个派生实体: Instructor(教员)和St ...

  3. Entity Framework 6 Recipes 2nd Edition(13-1)译 -> 优化TPT继承模型的查询

    问题 你想提高在一个TPT继承模型里的查询 解决方案 让我们假设有一个简单的TPT继承模型,如图Figure 13-1 Figure 13-1. A simple Table per Type inh ...

  4. Nginx 配置继承模型

    要了解nginx的继承模型,首先需要知道nginx使用多个配置块进行操作.在nginx中,这样的块被称为上下文,例如,放置在服务器上下文中的配置指令驻留在server { }块中,就像放置在http上 ...

  5. 浏览器环境下的javascript DOM对象继承模型

    这张图是我直接在现代浏览器中通过prototype原型溯源绘制的一张浏览器宿主环境下的javascript DOM对象模型,对于有效学习和使用javascript DOM编程起到高屋建瓴的指导作用, ...

  6. Odoo中的模型继承、视图继承、Qweb模板继承详解

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10826114.html 在实际开发过程中,经常会遇到需要修改Odoo原生逻辑的情况.然而,直接修改Odoo底 ...

  7. Django继承drf的user模型的demo

    1.安装虚拟环境 #mkvirtualenv drfdemo -p python3 #pip install django #pip install djangorestframework #pip ...

  8. Entity Framework 6 Recipes 2nd Edition(10-10)译 - > 为TPH继承的插入、更新、删除操作映射到存储过程

    10-10. 为TPH继承的插入.更新.删除操作映射到存储过程 问题 TPH继承模型,想把它的插入.修改.删除操作映射到存储过程 Solution 假设数据库有一个描述不同种类的产品表(Product ...

  9. 《Entity Framework 6 Recipes》中文翻译系列 (36) ------ 第六章 继承与建模高级应用之TPC继承映射

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 6-12  TPC继承映射建模 问题 你有两张或多张架构和数据类似的表,你想使用TP ...

随机推荐

  1. hive中关键字作为列名的方法

    hive中有很多关键字,直接作为列名,会出错的 例如 下面 user就是关键字,作为字段时报以下错误. 解决方案: 使用·· (ESC下面的那个键,点号)两个符号包裹即可.

  2. repcached与mysql缓存測试

    使用gem安装mysql引擎 gem install mysql(假设安装失败.请查找一些依赖组建是否安装,比如mysql-devel) 编写ruby脚本,先获取mysql数据,之后从memcache ...

  3. Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。

    Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务,Spring Cloud是一个基于Spring Boot实现的云应用开发工具:Spr ...

  4. 【BZOJ】1649: [Usaco2006 Dec]Cow Roller Coaster(dp)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1649 又是题解... 设f[i][j]表示费用i长度j得到的最大乐趣 f[i][end[a]]=ma ...

  5. Swift AVFoundation 二维码扫描和生成

    项目最终不须要支持iOS6了(泪崩),在二维码扫描这一块,可以全然的放弃ZXing库,改用系统的AVFoundation了,拿swift写了个Demo,效果例如以下: github地址:点这里 有关A ...

  6. digitalocean --- How To Install Apache Tomcat 8 on Ubuntu 16.04

    https://www.digitalocean.com/community/tutorials/how-to-install-apache-tomcat-8-on-ubuntu-16-04 Intr ...

  7. 数据库存入数据后id保持不变,或者直接报错

    数据库存入数据后id保持不变,且添加的数据一直在进行覆盖 或者直接报错 数据库存入数据后id保持不变,且添加的数据一直在进行覆盖 原因是: 之前注释掉了loadimage();在该函数中含有建立新的记 ...

  8. lua基础(一)

    参考链接: http://blog.csdn.net/lyh916/article/details/49719697 一.注释 --这是行注释 --[[ 这是块注释 这是块注释 这是块注释 --]] ...

  9. 如何使用phpmyadmin建立外键约束

    之前都是用sql语句进行的主外键的关联,现在用可视化的phpmyadmin感觉方便了很多,但是在做主外键约束的时候却十中找不到操作在哪里.网上搜索的也是千奇百怪五花八门的,都说的很晦涩,很多都说需要使 ...

  10. 【BZOJ5073】[Lydsy十月月赛]小A的咒语 DP(错解)

    [BZOJ5073][Lydsy十月月赛]小A的咒语 题解:沙茶DP,完全不用后缀数组. 用f[i][j]表示用了A的前i个字符,用了j段,最远能匹配到哪.因为显然我们能匹配到的地方越远越好,所以我们 ...