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

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

#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. 在C/C++中常用的符号

    C++中&和*的用法一直是非常让人头疼的难点,课本博客上讲这些知识点一般都是分开讲其用法的,没有详细的总结,导致我在这方面的知识结构格外混乱,在网上找到了一篇英文文章简单总结了这两个符号的一些 ...

  2. vue input 循环渲染问题

    <li> <span>下属区县:</span> <div class="quxianList" v-for="(qx,index ...

  3. Python 日志文件处理

    今天想把 Python 项目中的日志 保存到文件中. 找到了方法.非常简单 https://www.cnblogs.com/nancyzhu/p/8551506.html 1. logging.bas ...

  4. Mac10.14.6安装并破解PyCharm

    之前安装了PyCharm的Community版本, 用了半天之后发现好多功能都没有, 于是准备安装专业版然后破解. 安装包直接去官网下载, 不多说. 破解补丁的下载地址如下: 链接:https://p ...

  5. Jupyter修改工作目录(Anaconda环境)

    Anaconda安装时未添加环境变量 1.打开Anaconda Prompt 输入jupyter notebook --generate-config (base) C:\Users\Sroxi> ...

  6. 写CSDN博客

    文章目录 前言 写博客的规范 写博客的小技巧 版权声明模板 博客表格模板 更改博客字体和颜色 LaTeX 数学公式 前言 这是一篇关于写CSDN博客的文章.记录我的博客规范,技巧,模板,心得. 写博客 ...

  7. 5-6 c语言之【枚举,联合体,递归】

    今天学习了枚举,联合体,递归,重点在于递归,所以从网上找到了一些递归的题目进行练习,毕竟程序员界流传一句话,会用循环的是人,会用递归的是神,哈哈哈 还是按次序进行梳理,第一个枚举,枚举和宏定义很相似, ...

  8. Linux文件删除,但是df -hT之后磁盘空间没有释放

    Linux 磁盘空间总是报警,查到到大文件,删除之后,df看到磁盘空间并没有释放. 查找了下发现系统对rm进行了alias   ,因为Linux对删除操作没有回收站机制,对rm操作进行了自定义,对删除 ...

  9. elasticsearch 集群详解

    ES为什么要实现集群 在单台ES服务器节点上,随着业务量的发展索引文件慢慢增多,会影响到效率和内存存储问题等. 如果使用ES集群,会将单台服务器节点的索引文件使用分片技术,分布式的存放在多个不同的物理 ...

  10. Bitbucket入门手册

    老大要我去调研一下有什么好用的免费软件版本管理工具,有利于小团队开发的.我第一个想到的就是git,经常在git下东西,听说它的代码仓库好用,于是就注册了一个github的账号,创建仓库的时候才发现只能 ...