单一虚函数继承

class A
{
public:
virtual int foo( ) { return val ; }
virtual int funA( ) {}
private:
int val ;
char bit1 ;
} ;

class B : public A
{
public:
virtual int foo( ) { return bit2; }
virtual int funB( ) {}
private:
char bit2 ;
};

一个class只要有一个虚函数,那么每一个class object被安插上一个由编译器内部产生的指针,指向该表格(virtual table)。

virtual table 的第一项是表示class的类型。

因为,基类指针的特殊性,它可以指向基类对象,也可以指向派生类对象。故:ptr->foo( ) ;这种调用,我们需要知道ptr所指对象的真实类型。(就算不知道ptr所指对象的类型,也可以正确调用fun函数,但是由于fun函数有编译器插入的this指针,this指针要与ptr指向的对象地址正确对应,以正确访问对象中的成员变量,但ptr中却没有这样的信息)

virtual table 之后的表格是class中的每个虚函数地址。

一个class只会有一个virtual table。派生类的virtual table是在基类的virtual table上增加,修改的。

派生类中的虚函数会改写(overriding)与基类中同名且参数相同的虚函数,把virtual table表中相应的基类虚函数地址改写为相应派生类虚函数的地址。

以上工作都是由编译器完成的。执行期要做的就是在特定的virtual table表项中激活相应的虚函数,然后根据virtual table首项的类型信息,正确执行此虚函数。

例如:ptr->foo( ) ;

一般而言,我并不知道ptr所指对象的真正类型。然而我知道。经由ptr可以存取到该对象的virtual table。

虽然我不知道哪一个foo( )实体会被调用,但我知道每一个foo( )函数的地址都放在虚表的第二项。

故:根据以上信息,编译器可以将该调用转化为:

( *ptr->vptr[1] )( ptr ) ;

【注意:】基类指针虽然可以指向派生类,但是它实际上指向的是派生类中的基类部分。(这就不违反指针的特性了,指针类型与其指向范围是一致的)故基类指针不能访问派生类的成员。(但基类指针可以通过访问派生类的虚函数,间接操作派生类成员)

上面的内存图验证了这个例子,就是指的是指向派生类的指针,指针指向base class中没有被devived class继承的虚函数,或者指向devived class中重写base class中的虚函数,

多重继承

class A
{
public:
A() {}
virtual ~A() {}
virtual int foo( ) { return val ; }
virtual int funA( ) {}
private:
int val ;
char bit1 ;
} ; class B :
{
public:
B() {}
virtual ~B() {}
virtual int foo( ) { return bit2; }
virtual int funB( ) {}
private:
char bit2 ;
}; class Derived : public A, public B
{
public:
Derived() {}
virtual ~Derived() {}
virtual int foo( ) { return bit3; }
virtual int funDerived( ) {}
private:
char bit3 ;
};

注意:在多重继承下,若有n个基类,则派生类中有n个virtual table.

针对每一个virtual table,派生类对象中有对应的vptr。这些vptrs将在构造函数中被设立初值。

派生类的虚函数会覆盖(改写)其每个基类virtualtable中相应的虚函数索引值。

多重继承最左端的基类,在派生类中作为主要实体,其virtualtable为主要表格,其它基类的virtual table为次要表格。

当你将Derived对象地址指定给一个Base1指针或Derived指针时,被处理的virtualtable是主要表格vptr_Base1。

故主要表格要包含Derived的所有虚函数(包括继承得来的虚函数)。所以其中有Base1、Base2、Derived中的虚函数。

而其它次要表格中的项目数不变,只是有些虚函数的索引值被重写。

涉及多重继承的指针的转换

多重继承的问题主要发生于:派生类对象和其第二或后继的基类对象之间的转换。

【对一个多重派生对象,将其地址指定给“最左端(也就是第一个)”base class的指针,情况将和单一继承时相同,因为二者都指向相同的起始地址。需付出的成本只有地址的指定操作而已。至于第二个或后继的base class的地址指定操作,则需要将地址修改:加上(或减去)介于中间的base class subobject(s)大小】

例如:

Derived dObj ;

A* pA = &dObj ;

只需要简单地拷贝地址就行了。

而:

Derived* pd ;

B* pB = pd ;

需要这样的内部转化:

//虚拟C++码

pB = pd ? (B*)( (char*)pd + sizeof(A) ) : 0  ;

含虚继承的多重继承

class A {  } ;
class B : public virtual A { } ;
class C : public virtual A { } ;
class D : public B, public C { };

注意】class A { };实际上并不是空的,它有一个隐晦的1字节,那是被编译器安插进去的一个char。这是为了使得class A的对象得以在内存中配置独一无二的地址。

当语言支持虚基类时,就会导致一些额外负担。在派生类中会有一个额外指针,指针指向一个相关表格,表格中存放的或者是虚基类对象,或者是其偏移量。

若虚基类为空,则表格中存放的是虚基类对象(即一个安插字节)

VC++编译器的优化:

VC++特别对 空virtual baseclass做了处理。空虚基类的派生类中只有一个4字节的指针。没有安插char,也没有字节填充。

(因为安插char的目的是为了空类实例化的对象在内存中有地址,而现在其派生类对象中已经有个指针了,其可以取地址,故不需要安插char了)

注意:如果虚基类中有数据成员,则两种编译器(“有特殊处理者”和“无特殊处理者”)就会产生完全相同的对象布局。

2、虚基类有数据成员

例:

class A
{
public:

private:
int x, y ;
} ; class B : public virtual A
{
public:

private:
int valB ;
} ; class C : public virtual A
{
public:

private:
int valC ;
} ; class D : public B, public C
{
public:

private:
int valD ;
};

最终的派生类对象底部是共享虚基类的部分,派生类class B、class C部分的指针指向的虚函数表的前面增加了一项:offset(从对象的开头算起,到共享虚基类部分的字节数)

这样可以快速地访问到共享虚基类的成员。

问:

①为什么虚继承不像一般继承那样,把基类成员放在顶部,把新增派生类成员放在尾部?

我想是:因为在最后的多重继承时,要求最终的派生类对象中只有一份基类成员,故最终派生类会从其各个父类中提取它们各自的派生数据成员。若按一般继承那样的对象模型,很容易找到父类中的派生类成员,但难以确定边界,故难以提取数据。为了提取数据方便,把基类成员放在尾部。

②为什么虚继承的派生类中有两个虚指针?

我想是:因为要实现多态。当基类指针指向派生类中的基类部分时,必须要有指向虚表的指针,才能实现多态。

注意:class D对象模型与一般多重继承的派生类对象的布局相似,多重继承最左边的基类部分的虚表是“主要表格”。故class B、class C、class A部分的虚表各不相同。它们都是为了实现多态。

【编程风格】一般而言,virtual base class最有效的一种运用形式是:一个抽象的virtual base class,没有任何数据成员。

c++ devived object model的更多相关文章

  1. Selenium的PO模式(Page Object Model)[python版]

     Page Object Model 简称POM  普通的测试用例代码: .... #测试用例 def test_login_mail(self): driver = self.driver driv ...

  2. 在C#开发中如何使用Client Object Model客户端代码获得SharePoint 网站、列表的权限情况

    自从人类学会了使用火,烤制的方式替代了人类的消化系统部分功能,从此人类的消化系统更加简单,加速了人脑的进化:自从SharePoint 2010开始有了Client Side Object Model ...

  3. Selenium的PO模式(Page Object Model)|(Selenium Webdriver For Python)

            研究Selenium + python 自动化测试有近两个月了,不能说非常熟练,起码对selenium自动化的执行有了深入的认识. 从最初无结构的代码,到类的使用,方法封装,从原始函数 ...

  4. 解决在使用client object model的时候报“object does not belong to a list”错误

    在查看别人代码的时候,发现了个有意思的问题,使用client object model将一个文件check in 我使用的是如下语句获取file Microsoft.SharePoint.Client ...

  5. Page Object Model (Selenium, Python)

    时间 2015-06-15 00:11:56  Qxf2 blog 原文  http://qxf2.com/blog/page-object-model-selenium-python/ 主题 Sel ...

  6. SharePoint Client Object Model API 介绍以及工作原理解析

    CSOM和ServerAPI 的对比 SharePoint从2010开始引入了Client Object Model的API(后文中用CSOM来代替),从名字来看,我们可以简单的看出,该API是面向客 ...

  7. BOM (Browser Object Model) 浏览器对象模型

    l对象的角色,因此所有在全局作用域中声明的变量/函数都会变成window对象的属性和方法; // PS:尝试访问未声明的变量会抛出错误,但是通过查询window对象,可以知道某个可能未声明的对象是否存 ...

  8. 文本对象模型(Document Object Model)

    本文内容: 1. 概述 2. DOM中的节点类型 3. DOM节点的选取 4. 存取元素属性 5.DOM元素的增删 6.小结 ★ 概述 文本对象模型(DOM)是一个能够让程序和脚本动态访问和更新文档内 ...

  9. 浏览器对象模型(BOM,Browser Object Model)

    本文内容     1.概述     2.windows与document     3.对话框     4.定时调用     5.URL解析与访问历史     6.浏览器和屏幕信息 ★概述     &q ...

随机推荐

  1. idea中不重启服务器更改代码(使用jrebel)

    http://139.199.89.239:1008/88414687-3b91-4286-89ba-2dc813b107ce 第一步 第二步:下载jrebel 第三步(这里有些有有些没有) 下载完后 ...

  2. Git基础篇

    对于Git的一些基础了解,安装,里面的一些名词,这里就不做介绍了.主要记录怎么使用GIt. 本篇介绍: 配置个人信息        生成本地仓库并与远程库相连        添加SSH秘钥       ...

  3. caffe中各种cblas的函数使用总结

    转来的,来自:http://www.cnblogs.com/huashiyiqike/p/3886670.html 总结的很赞,转到这里,留一下笔记.感觉cblas的函数名字很好记的,试着去找过源代码 ...

  4. python基本使用时常见错误

    python基本使用时常见错误 字符编码错误 如果要学习计算机编程语言,首先就要搞懂字符编码,否则在以后的学习过程中,将会是一场噩梦.在一开始使用的时候,我就遇到了很多的关于字符编码的问题,做个简单的 ...

  5. SpringBoot学习8:springboot整合freemarker

    1.创建maven项目,添加pom依赖 <!--springboot项目依赖的父项目--> <parent> <groupId>org.springframewor ...

  6. 异构数据库迁移——DATAX

    背景 在最近接触到的一个case里面,需要把db2的数据迁移至oracle,客户可接收的停机时间为3小时. 同步方式的比较 一说到停机时间,大家第一时间想到Oracle公司的GoldenGate实时同 ...

  7. Spring框架基础2

    Spring框架基础2 测试Spring的AOP思想和注解的使用 导包(在前面的基础上添加) SpringAOP名词解释 AOP编程思想:横向重复代码,纵向抽取:就是说多个地方重复的代码可以抽取出来公 ...

  8. scrapy--json(喜马拉雅Fm)

    已经开始听喜马拉雅Fm电台有2个月,听里面的故事,感觉能听到自己,特别是蕊希电台,始于声音,陷于故事,忠于总结.感谢喜马拉雅Fm陪我度过了这2个月,应该是太爱了,然后就开始对Fm下手了.QAQ 该博客 ...

  9. python--Wrapper

    然后给大家介绍的是Wrapper(装饰器),使用广泛.python笔试,面试的话也会百分百问到的,基础和中级的知识储备还是必用的. 让我们开始. 先来一些基础相关知识 *args,**kwargs的区 ...

  10. php 变量的8类类型

    整形,布尔,浮点形,字符串,数组,资源,对象和null php数据类型之查看和判断数据类型 php数据类型之自动转换和强制转换