在前面已经探讨过了虚继承对类的大小的影响,这次来加上虚函数和虚继承对类的大小的影响。

先来回顾一下之前例子的代码:

#include <iostream>
using namespace std; class BB {
public:
int bb_;
}; class B1 : virtual public BB {
public:
int b1_;
}; class B2 : virtual public BB {
public:
int b2_;
}; class DD : public B1, public B2 {
public:
int dd_;
}; int main(void) {
cout<<sizeof(BB)<<endl;
cout<<sizeof(B1)<<endl;
cout<<sizeof(DD)<<endl; B1 b1;
long** p;
cout<<&b1<<endl;//类的首地址
cout<<&b1.bb_<<endl;
cout<<&b1.b1_<<endl; p = (long**)&b1;//这表示是vbptr
cout<<p[][]<<endl;//取出vbptr指向的vbtl的第一个数据项
cout<<p[][]<<endl;//取出vbptr指向的vbtl的第二个数据项 DD dd;
cout<<&dd<<endl;//类的首地址
cout<<&dd.bb_<<endl;
cout<<&dd.b1_<<endl;
cout<<&dd.b2_<<endl;
cout<<&dd.dd_<<endl;
p = (long**)&dd;
cout<<p[][]<<endl;
cout<<p[][]<<endl;
cout<<p[][]<<endl;
cout<<p[][]<<endl; dd.bb_ = ; BB* pp;
pp = &dd;
pp->bb_;//这是通过间接访问,需要运行时的支持 return ;
}

编译运行:

而数据模型为:

关于对虚继承的详细分析可以参考博文:http://www.cnblogs.com/webor2006/p/5621825.html

下面在这个例子上加上虚函数来进一步讨论数据模型:

#include <iostream>
using namespace std; class BB {
public:
virtual void vfbb() {
cout<<"BB::vfbb()"<<endl;
} virtual void vfbb2() {
cout<<"BB::vfbb2()"<<endl;
} int bb_;
}; class B1 : virtual public BB {
public:
virtual void vfb1() {
cout<<"B1::vfb1()"<<endl;
} int b1_;
}; class B2 : virtual public BB {
public:
virtual void vfb2() {
cout<<"B2::vfb2()"<<endl;
}
int b2_;
}; class DD : public B1, public B2 {
public:
virtual void vfdd() {
cout<<"DD::vfdd()"<<endl;
} int dd_;
}; int main(void) {
cout<<sizeof(BB)<<endl;
cout<<sizeof(B1)<<endl;
cout<<sizeof(DD)<<endl; //B1 b1;
//long** p;
//cout<<&b1<<endl;//类的首地址
//cout<<&b1.bb_<<endl;
//cout<<&b1.b1_<<endl; //p = (long**)&b1;//这表示是vbptr
//cout<<p[0][0]<<endl;//取出vbptr指向的vbtl的第一个数据项
//cout<<p[0][1]<<endl;//取出vbptr指向的vbtl的第二个数据项 //DD dd;
//cout<<&dd<<endl;//类的首地址
//cout<<&dd.bb_<<endl;
//cout<<&dd.b1_<<endl;
//cout<<&dd.b2_<<endl;
//cout<<&dd.dd_<<endl;
//p = (long**)&dd;
//cout<<p[0][0]<<endl;
//cout<<p[0][1]<<endl;
//cout<<p[2][0]<<endl;
//cout<<p[2][1]<<endl; return ;
}

编译运行:

下面还是先画一下它的内存模型,然后再用代码来验证:

先来看下BB类:

编译运行:

下面来分析一下B1类:

下面来验证:

typedef void (*FUNC)();

int main(void) {
cout<<sizeof(BB)<<endl;
cout<<sizeof(B1)<<endl;
cout<<sizeof(DD)<<endl; BB bb;
long** p = (long**)&bb;
FUNC fun;
fun = (FUNC)p[][];
fun();
fun = (FUNC)p[][];
fun(); cout<<"----------------------------"<<endl; B1 b1;
p = (long**)&b1;
fun = (FUNC)p[0][0];
fun();
cout<<p[1][0]<<endl;
cout<<p[1][1]<<endl;
fun = (FUNC)p[3][0];
fun();
fun = (FUNC)p[3][1];
fun(); //DD dd;
//cout<<&dd<<endl;//类的首地址
//cout<<&dd.bb_<<endl;
//cout<<&dd.b1_<<endl;
//cout<<&dd.b2_<<endl;
//cout<<&dd.dd_<<endl;
//p = (long**)&dd;
//cout<<p[0][0]<<endl;
//cout<<p[0][1]<<endl;
//cout<<p[2][0]<<endl;
//cout<<p[2][1]<<endl; return ;
}

编译运行:

下面再来分析一下DD类:

同样用代码来验证:

typedef void (*FUNC)();

int main(void) {
cout<<sizeof(BB)<<endl;
cout<<sizeof(B1)<<endl;
cout<<sizeof(DD)<<endl; BB bb;
long** p = (long**)&bb;
FUNC fun;
fun = (FUNC)p[][];
fun();
fun = (FUNC)p[][];
fun(); cout<<"----------------------------"<<endl; B1 b1;
p = (long**)&b1;
fun = (FUNC)p[][];
fun();
cout<<p[][]<<endl;
cout<<p[][]<<endl;
fun = (FUNC)p[][];
fun();
fun = (FUNC)p[][];
fun(); cout<<"----------------------------"<<endl; DD dd;
p = (long**)&dd;
fun = (FUNC)p[0][0];
fun();
cout<<p[1][0]<<endl;
cout<<p[1][1]<<endl;
fun = (FUNC)p[3][0];
fun();
cout<<p[4][0]<<endl;
cout<<p[4][1]<<endl;
fun = (FUNC)p[7][0];
fun();
fun = (FUNC)p[7][1];
fun(); return ;
}

编译运行:

有了虚继承和虚函数的类的内存模型是比较复杂的,需细细体会。下面来讨论一个新的东东:

对于C++的数据模型,实际上它还包括另外一些信息,也就是RTTI,以便在运行时进行类型识别,C++的运行时类型识别主要是由dynamic_cast运算符、typeid运算符、type_info来支持下,下面具体来学习下:

#include <iostream>
using namespace std; class Shape {
public:
virtual void draw() = ;
virtual ~Shape() { }
}; class Circle : public Shape {//圆形
public:
void draw() {
cout<<"Circle::draw ..."<<endl;
}
}; class Square : public Shape {//正方形
public:
void draw() {
cout<<"Square::draw ..."<<endl;
}
}; int main(void) {
Shape* p;
Circle c; p = &c;
p->draw(); return ;
}

编译运行:

以上输出毫无疑问,这时可以用dynamic_cast运算符来进行类型识别:

#include <iostream>
using namespace std; class Shape {
public:
virtual void draw() = ;
virtual ~Shape() { }
}; class Circle : public Shape {//圆形
public:
void draw() {
cout<<"Circle::draw ..."<<endl;
}
}; class Square : public Shape {//正方形
public:
void draw() {
cout<<"Square::draw ..."<<endl;
}
}; int main(void) {
Shape* p;
Circle c; p = &c;
p->draw(); if(dynamic_cast<Circle*>(p)) {
cout<<"p is point to a Circle Object"<<endl;
} else if(dynamic_cast<Square*>(p)) {
cout<<"p is point to a Square Object"<<endl;
} else {
cout<<"p is point to a Other Object"<<endl;
} return ;
}

编译运行:

这时就可以做安全的向下转型:

这里要提醒一下:在VS C++中要想支持这个类型识别,需要进行一个设置才行,否则是不支持的:

如果选择“否(/GR-)”,再次运行则会给出警告了:

如果运行的话会直接报错的:

所以还是需要将其打开,将配置还原才行。

现在已经学到几个转换相关的函数了,下面来总结一下:

static_cast:用在编译器认可的转型。

reinterpret_cast:用在编译器不认可的转型。

const_cast:去除常量性。

以上三个都是静态转型,不需要运行时支持。

而这里用的的dynamic_cast是安全向下转型,是动态转型,需要运行时的支持。

对于这个运算符它返回的是type_info对象,它的结构如下:

class type_info {
public:
virtual ~type_info();
bool operator==(const type_info& rhs) const;
bool operator!=(const type_info& rhs) const;
int before(const type_info& rhs) const;
const char* name() const;//它就代表类型的实际名,可以用它来判断类型
const char* raw_name() const;
private:
void *_m_data;
char _m_d_name[];
type_info(const type_info& rhs);
type_info& operator=(const type_info& rhs);
static const char _Name_base(const type_info *,__type_info_node* __ptype_info_node);
};

下面用它来打印一下:

编译运行:

所以就可以这样来写判断语句:

int main(void) {
Shape* p;
Circle c; p = &c;
p->draw(); if(dynamic_cast<Circle*>(p)) {
cout<<"p is point to a Circle Object"<<endl;
Circle* cp = dynamic_cast<Circle*>(p); //安全向下转型
cp->draw();
} else if(dynamic_cast<Square*>(p)) {
cout<<"p is point to a Square Object"<<endl;
} else {
cout<<"p is point to a Other Object"<<endl;
} //typeid运算符
cout<<typeid(*p).name()<<endl;
cout<<typeid(Circle).name()<<endl;
if(typeid(*p).name() == typeid(Circle).name()) {
cout<<"p is point to a Circle Object"<<endl;
((Circle*)p)->draw();
}else if(typeid(*p).name() == typeid(Square).name()) {
cout<<"p is point to a Square Object"<<endl;
((Square*)p)->draw();
} else {
cout<<"p is point to a Other Object"<<endl;
} return ;
}

编译运行:

【注意】:

①、由于type_info的构造函数和=号运算符是私有的:

所以不能这样写:

所以代码中是“typeid(Circle).name()”直接来用。

②、这样的类型转换都没有通过多态来访问效率来得高:

③、reinterpret_cast和C风格的强制转让换换还是有区别的:

c++对象模型和RTTI(runtime type information)的更多相关文章

  1. C++ - RTTI(RunTime Type Information)执行时类型信息 具体解释

    RTTI(RunTime Type Information)执行时类型信息 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details ...

  2. RTTI (Run-time type information) in C++

    In C++, RTTI (Run-time type information) is available only for the classes which have at least one v ...

  3. RTTI(Runtime Type Information )

    RTTI 是“Runtime Type Information”的缩写,意思是:运行时类型信息.它提供了运行时确定对象类型的方法.本文将简略介绍 RTTI 的一些背景知识.描述 RTTI 的概念,并通 ...

  4. c++ RTTI(runtime type info)

    RTTI(Run-Time Type Information,通过运行时类型信息)程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型. RTTI提供了以下两个非常有用的操作符: ...

  5. Dynamic type checking and runtime type information

    动态类型的关键是将动态对象与实际类型信息绑定. See also: Dynamic programming language and Interpreted language Dynamic type ...

  6. BEGINNING SHAREPOINT&#174; 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 Windows Phone

    BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 Windows Phone         和.NET托管代码和 ...

  7. BEGINNING SHAREPOINT&#174; 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 client对象模型API范围

    BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 client对象模型API范围         本章之前提到过. ...

  8. BEGINNING SHAREPOINT&#174; 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 client对象模型(CSOM)基础

    BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览  client对象模型(CSOM)基础         在SP2 ...

  9. BEGINNING SHAREPOINT&#174; 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 介绍SP2013中远程APIs

    BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览  介绍SP2013中远程APIs         当SP首次開始 ...

随机推荐

  1. Spring 使用单选按钮

    模型层需要提供数据选项,设置错误信息 关键代码 @NotNull(message = "请选择性别") private String gender; 控制器层需要在显示视图前,通过 ...

  2. (4.36)sql server中的waitfor

    关键词:waitfor SQL有定时执行的语句 WaitFor,可以写到一个存储过程中再执行一次 语法:WaitFor{Delay 'time'|Time 'time} Delay后面的时间为延迟多少 ...

  3. Fiddler之基础:面板、图标介绍

    1.面板介绍:菜单栏,工具栏,回话面板,监控面板 2.工具栏-图标 3.会话面板-图标 4.监控面板 5.状态栏 控制台Fiddler的左下角有一个命令行工具叫做QuickExec,允许你直接输入命令 ...

  4. Postman和jmeter的区别

    1.创建接口用例集(没区别) Postman是Collections,Jmeter是线程组,没什么区别. 2.步骤的实现(有区别) Postman和jmeter都是创建http请求 区别1:postm ...

  5. PAT(B) 1006 换个格式输出整数(Java)

    题目链接:1006 换个格式输出整数 (15 point(s)) 代码 /** * Score 15 * Run Time 153ms * @author wowpH * @version 1.1 * ...

  6. Spring (2)框架

    Spring第二天笔记 1. 使用注解配置Spring入门 1.1. 说在前面 学习基于注解的IoC配置,大家脑海里首先得有一个认知,即注解配置和xml配置要实现的功能都是一样的,都是要降低程序间的耦 ...

  7. gmpy安装使用方法

    gmpy是一种C编码的Python扩展模块,提供对GMP(或MPIR)多精度算术库的访问.gmpy 1.17是1.x系列的最终版本,没有进一步的更新计划.所有进一步的开发都在2.x系列(也称为gmpy ...

  8. IntelliJ IDEA调出problem窗口

    一.File =>Settings 二.搜索Compiler=>勾选Make project automatically 三.出现问题Problems窗口会报错 原文地址:https:// ...

  9. java基础知识学习 内存相关

    Java 内存分配策略 静态存储区(方法区):主要存放静态数据.全局 static 数据和常量.这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在. 栈区 :当方法被执行时,方法体内的局部 ...

  10. (七)Redis之持久化之RDB方式

    一.持久化概念 所有的数据都存在内存中,从内存当中同步到硬盘上,这个过程叫做持久化过程. 使用方法: 1. rdb持久化方法:在指定的时间间隔写入硬盘 2.         aof方式:将以日志,记录 ...