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

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

#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. WIN10激活教程,亲测,有效

    WIN10激活教程: 1.打开开始菜单,找到设置,点开“更新和安全”,切换到“激活”选项卡,查看到当前系统的激活状态 2.在搜索框输入“CMD”,出现“命令提示符”工具时,右击选择“以管理员身份”运行 ...

  2. mysql 查询结果为null 或 空字符串时,返回指定字符串

    直接上代码, 亲测可用: SELECT IF ( ifnull( 字段, '' ) = '', '返回的字符串', 字段) AS 别名(或者不要也可以) FROM table

  3. WCF-简单 配置文件

    一.服务端配置文件 主要包括 1.services 配置服务节点 <!--name 指的是契约实现类--> <service name="WcfLib.User2" ...

  4. html5 外链式实现加减乘除

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  5. 在Angular中使用$ compile

    转载自:http://odetocode.com/blogs/scott/archive/2014/05/07/using-compile-in-angular.aspx 在AngularJS中创建一 ...

  6. C# Winform 设置窗口打开的特效

    https://www.cnblogs.com/mq0036/p/6421946.html using System.Runtime.InteropServices; public class Win ...

  7. Nginx实现同一端口HTTP跳转HTTPS

    小目标:在只监听一个端口的情况下,将http访问跳转为https. 一般情况下http协议使用80端口,https协议443端口.要实现http强制转https是非常简单的事,随便都可以找到很多方案. ...

  8. Bat 批处理启动和停止Oracle 服务

    实际情况 * 不想开机自启动oracle服务,因为Windows 没有固态硬盘本身启动就很慢了,然后也不想自己手动的方式去启东oracle 服务 解决方案 *1启动 ``` @echo off ech ...

  9. Tomcat 和web 服务器配置

    mkdir /usr/local/tomcat # cd /usr/local/tomcat # tar -zxvf /software/apache-tomcat-7.0.54.tar.gz 生成链 ...

  10. opencv-04--图像金字塔

    图像金字塔被广泛应用于各种视觉应用中.图像金字塔是一个图像集合,集合中图像都源于同一个原始图像,而且是通过对原始图像连续降采样获得,直到达到某个中止条件才停止降采样.(当然,降为一个像素肯定是中止条件 ...