c++对象模型和RTTI(runtime type information)
在前面已经探讨过了虚继承对类的大小的影响,这次来加上虚函数和虚继承对类的大小的影响。
先来回顾一下之前例子的代码:
#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**)ⅆ
cout<<p[][]<<endl;
cout<<p[][]<<endl;
cout<<p[][]<<endl;
cout<<p[][]<<endl; dd.bb_ = ; BB* pp;
pp = ⅆ
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**)ⅆ
//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**)ⅆ
//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**)ⅆ
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)的更多相关文章
- C++ - RTTI(RunTime Type Information)执行时类型信息 具体解释
RTTI(RunTime Type Information)执行时类型信息 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details ...
- 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 ...
- RTTI(Runtime Type Information )
RTTI 是“Runtime Type Information”的缩写,意思是:运行时类型信息.它提供了运行时确定对象类型的方法.本文将简略介绍 RTTI 的一些背景知识.描述 RTTI 的概念,并通 ...
- c++ RTTI(runtime type info)
RTTI(Run-Time Type Information,通过运行时类型信息)程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型. RTTI提供了以下两个非常有用的操作符: ...
- Dynamic type checking and runtime type information
动态类型的关键是将动态对象与实际类型信息绑定. See also: Dynamic programming language and Interpreted language Dynamic type ...
- BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 Windows Phone
BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 Windows Phone 和.NET托管代码和 ...
- BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 client对象模型API范围
BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 client对象模型API范围 本章之前提到过. ...
- BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 client对象模型(CSOM)基础
BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 client对象模型(CSOM)基础 在SP2 ...
- BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 介绍SP2013中远程APIs
BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 介绍SP2013中远程APIs 当SP首次開始 ...
随机推荐
- Spring 多对对实体
package com.wangshenghua.entity; import java.io.Serializable; import java.util.Set; import javax.per ...
- python不定长参数 *argc,**kargcs(19)
在 python函数的声明和调用 中我们简单的了解了函数的相关使用,然而在函数传递参数的时候,我们埋下了一个坑,关于不定长参数的传递我们还没有讲,今天这篇文章主要就是讲解这个问题. 一.函数不定长参数 ...
- Java 文件下载工具类
Java 文件下载工具类 import org.slf4j.Logger; import org.slf4j.LoggerFactory; private static Logger logger = ...
- Redis 集群_主从复制_哨兵模型
1 redis集群简介 1.1 集群的概念 所谓的集群,就是通过添加服务器的数量,提供相同的服务,从而让服务器达到一个稳定.高效的状态. 1.1.1 使用redis集群的必要性 问题:我们已经部署好了 ...
- 系统集成Facebook授权发布帖子以及获取帖子评论等功能
公司的业务和海外贸易紧密连接,项目中需要对接Facebook.Google.Twitter相关API,下面详细描述一下我们对接Facebook中遇到的问题 1,注册Facebook账户,Faceboo ...
- 利用Python进行数据分析_Pandas_汇总和计算描述统计
申明:本系列文章是自己在学习<利用Python进行数据分析>这本书的过程中,为了方便后期自己巩固知识而整理. In [1]: import numpy as np In [2]: impo ...
- PAT(B) 1021 个位数统计(Java)
题目链接:1021 个位数统计 (15 point(s)) 代码 /** * Score 15 * Run Time 93ms * @author wowpH * @version 1.0 */ im ...
- UPUPW Apache5.5系列本地开发环境配置
UPUPW Apache5.5系列 1. 在官网下载 Apache5.5系列,选择云端下载. 官网地址: http://www.upupw.net/aphp55/n110.html 2. 下载后,将压 ...
- 【C#】上机实验一
1.开发一个控制台应用程序,根据提示从键盘获取一个华氏温度,请转换并输出对应的摄氏温度. using System; namespace Project { class Program { publi ...
- 定义别名:typedef和using
定义别名: