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

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

#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. git 创建标签推送远程分支

    目录 git 创建标签推送远程分支 查看tag 创建tag 推送标签到远程仓库 删除tag git 创建标签推送远程分支 标签概念: tag, 对于迭代频繁的项目. 每一个标签可以理解为一个版本. 创 ...

  2. udevdm命令详解

    udevadm 后接一个命令和命令指定选项.它控制了udev运行的行为,处理内核事件,控制事件队列,并且提供简单的调试机制. 选项: --debug 打印错误信息 --version 打印版本信息 - ...

  3. python 计算文件夹里所有内容的大小总和

    计算文件夹里所有内容的大小总和 递归方法 '''计算文件夹的大小''' import os def dir_file_size(path): if os.path.isdir(path): file_ ...

  4. 【转帖】iPhone 11 Pro Max皇帝版物料成本不足3500元 卖一赚二

    iPhone 11 Pro Max皇帝版物料成本不足3500元 卖一赚二 https://www.cnbeta.com/articles/tech/894449.htm 供应链的掌控力很重要 苹果今年 ...

  5. js实现——鼠标移动时跟随着一连的小图片

    首先放置一连的image <body> <div><img src="yezi.png" alt="tu"></div ...

  6. python 手机App数据抓取实战二抖音用户的抓取

    前言 什么?你问我国庆七天假期干了什么?说出来你可能不信,我爬取了cxk坤坤的抖音粉丝数据,我也不知道我为什么这么无聊. 本文主要记录如何使用appium自动化工具实现抖音App模拟滑动,然后分析数据 ...

  7. 剑指offer(3)——二维数组中的查找

    题目: 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数 思路: 首先选取数组中右上 ...

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

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

  9. Luogu4022 CTSC2012熟悉的文章(广义后缀自动机+二分答案+动态规划+单调队列)

    对作文库中的串建出广义SAM,然后显然可以二分答案,二分之后考虑暴力dp,设f[i]为前i位最长匹配长度,显然有f[i]=max(f[i-1],f[j]+i-j) (i-j>=l&&am ...

  10. 解决GitHub下载速度缓慢的问题

    随着微软大大宣布GitHub针对个人用户的仓库免费,相信每位开发者都感受到了"真香". 然而因为一些众所周知的原因,国内访问GitHub总会遇到下载速度缓慢.链接意外终止的情况. ...