C++超详细讲解RTTI和cast运算符的使用

目录
  • 1. RTTI
    • 1.1 dynamic_cast运算符
    • 1.2 typeid运算符
  • 2. cast运算符

1. RTTI

RTTI是运行阶段类型识别(Running Type Identificarion)的简称。

如何知道指针指向的是哪种对象?

这是个很常见的问题,由于我们允许使用基类指针指向派生类,所以基类指针指向的对象可能是基类对象,也可能是派生类对象。但是我们需要知道对象种类,因为我们需要使用正确的类方法。

RTTI能解决上述问题。

1.1 dynamic_cast运算符

dynamic_cast是最常用的RTTI组件,它不能回答"指针指向的是哪类对象",但是它能回答"是否可以安全的将对象的地址赋给特定类型指针"。

什么是安全?

例如:A是基类,B是A的派生类,C是B的派生类。那么将BC对象的地址赋给A指针是安全的,而反向就是不安全的。因为公有继承满足is-a关系,指针和引用进行向上转换是安全的,向下转换就是不安全的。

dynamic_cast的用法是:

dynamic_cast<Type*>(ptr)dynamic_cast<Type&>(ref)

可以看到的是,dynamic_cast是一种类型转换符,它支持指针间的转换,他将ptr的类型转换成Type*,如果这种转换是安全的,那会返回转换后的指针;如果不安全那就会返回空指针。对于引用的话,他将ref的类型转换成Type&,如果这种转换是安全的,那就返回转换后的引用;如果不安全那就会引发bad_cast异常。

总之,dynamic_cast类型转换运算符比C风格的类型转换要安全的多,而且它能够判断转换是否安全。

//RTTI1.cpp
#include<iostream>
#include<cstdlib>
#include<ctime>
using namespace std;
class Grand
{
protected:
    int hold;
public:
    Grand(int h=0):hold(h){};
    virtual void Speak() const {cout<<"I am Grand class!\n";}
};
class Superb:public Grand
{
public:
    Superb(int h=0):Grand(h){}
    virtual void Speak() const {cout<<"I am a superb class!\n";}
    virtual void Say() const {cout<<"the value: "<<Grand::hold<<endl;}
};
class Magnificent:public Superb
{
private:
    char ch;
public:
    Magnificent(int h=0,char c='A'):Superb(h),ch(c){}
    virtual void Speak() const{cout<<"I am a Magnificent class!\n";}
    virtual void Say() const{cout<<"the character: "<<ch<<endl<<"the value: "<<Grand::hold<<endl;}
};
Grand *GetOne();
int main()
{
    srand(time(0));
    Grand *pg;
    Superb *ps;
    for(int i=0;i<5;i++)
    {
        pg=GetOne();
        pg->Speak();
        if(ps=dynamic_cast<Superb*>(pg))
        {
            ps->Say();
        }
    }
    delete pg;
    return 0;
}
Grand * GetOne()
{
    Grand *p;
    switch (rand()%3)
    {
        case 0:
            p=new Grand(rand()%100);
            break;
        case 1:
            p=new Superb(rand()%100);
            break;
        case 2:
            p=new Magnificent(rand()%100,'A'+rand()%26);
            break;
    }
    return p;
}

I am a Magnificent class!
the character: I
the value: 74
I am a superb class!
the value: 50
I am Grand class!
I am Grand class!
I am a Magnificent class!
the character: V
the value: 99

上面这个例子说明了重要的一点:尽可能使用虚函数,而只在必要时使用RTTI。

如果将dynamic_cast用于引用,当请求不安全的时候,会引发bad_cast异常,这种异常时从exception类派生而来的,它在头文件typeinfo中定义。

则我们可以使用如下方式处理异常:

#include<typeinfo>
...
try{
    Superb & rs= dynamic_cast<Superb &>(rg);
    ....
}
catch(bad_cast &)
{
    ...
};

1.2 typeid运算符

type_info类是在头文件typeinfo中定义的一个类。type_info类重载了==!=运算符。

typeid是一个运算符,它返回一个对type_info的引用,它可以接受2种参数:类名和对象。typeid是真正回答了"指针指向的是哪类对象"。

(实际上,typeid也可以用于其他类型,例如结构体,函数指针,各种类型,只要sizeof能接受的,typeid都能接受)

用法:

typeid(Magnificent)==typeid(*pg)

如果pg是一个空指针,则会引发bad_typeid异常。

type_info类中包含一个name()接口,该接口返回字符串,一般情况下是类的名称。

cout<<typeid(*pg).name()<<".\n"

例子:

//RTTI2.cpp
#include<iostream>
#include<cstdlib>
#include<ctime>
#include<typeinfo>
using namespace std;
class Grand
{
protected:
    int hold;
public:
    Grand(int h=0):hold(h){};
    virtual void Speak() const {cout<<"I am Grand class!\n";}
};
class Superb:public Grand
{
public:
    Superb(int h=0):Grand(h){}
    virtual void Speak() const {cout<<"I am a superb class!\n";}
    virtual void Say() const {cout<<"the value: "<<Grand::hold<<endl;}
};
class Magnificent:public Superb
{
private:
    char ch;
public:
    Magnificent(int h=0,char c='A'):Superb(h),ch(c){}
    virtual void Speak() const{cout<<"I am a Magnificent class!\n";}
    virtual void Say() const{cout<<"the character: "<<ch<<endl<<"the value: "<<Grand::hold<<endl;}
};
Grand *GetOne();
int main()
{
    srand(time(0));
    Grand *pg;
    Superb *ps;
    for(int i=0;i<5;i++)
    {
        pg=GetOne();
        cout<<"Now processing type "<<typeid(*pg).name()<<".\n";
        pg->Speak();
        if(ps=dynamic_cast<Superb*>(pg))
        {
            ps->Say();
        }
        if(typeid(Magnificent)==typeid(*pg))
        {
            cout<<"Yes,you are really magnificent.\n";
        }
    }
    delete pg;
    return 0;
}
Grand * GetOne()
{
    Grand *p;
    switch (rand()%3)
    {
        case 0:
            p=new Grand(rand()%100);
            break;
        case 1:
            p=new Superb(rand()%100);
            break;
        case 2:
            p=new Magnificent(rand()%100,'A'+rand()%26);
            break;
    }
    return p;
}

Now processing type 6Superb.      
I am a superb class!
the value: 64
Now processing type 11Magnificent.
I am a Magnificent class!
the character: Y
the value: 30
Yes,you are really magnificent.   
Now processing type 5Grand.       
I am Grand class!
Now processing type 11Magnificent.
I am a Magnificent class!
the character: C
the value: 3
Yes,you are really magnificent.   
Now processing type 5Grand.       
I am Grand class!

注意,你可能发现了typeid远比dynamic_cast优秀的多,typeid会直接告诉你类型是什么,而dynamic_cast只告诉你是否可以类型转换。但是typeid的效率比dynamic_cast低很多,所以,请优先考虑dynamic_cast和虚函数,如果不行才使用typeid

2. cast运算符

C语言中的类型转换符太过随意,C++创始人认为,我们应该使用严格的类型转换,则C++提供了4个类型转换运算符:

  • dynamic_cast
  • const_cast
  • static_cast
  • reinterpret_cast

dynamic_cast运算符已经介绍过了,它的用途是,使得类层次结构中进行向上转换,而不允许其他转换。

const_cast运算符,用于除去或增加 类型的constvolatile属性。

它的语法是:

const_cast<type-name>(expression)

如果类型的其他方面也被修改,则上述类型转换就会出错,也就是说,除了cv限定符可以不同外,type_nameexpression的类型必须相同。

提供该运算符的目的是:有时候我们需要一个值:它在大多数情况下是常量,而有时候我们需要更改它。

看一个有趣的例子:

//cast运算符1.cpp
//cast运算符1.cpp
#include <iostream>
int main()
{
    using std::cout;
    using std::endl;
    volatile const int a=100;
    volatile const int & ra=a;//无法通过ra修改a
    int &change_a=const_cast<int &>(ra);//可以通过change_a修改a;
    change_a=255;
    cout<<a<<endl;
}

上面最后这个a常量被修改成255,这是我们预期的结果。但是如果我把volatile关键词去掉,那么a的值还是100。其实是编译器在这里玩了个小聪明,const int a=100,编译器认为a就不会发生改变了,所以就把a放在了寄存器中,因为访问寄存器要比访问内存单元快的多,每次都从寄存器中取数据,但是后来a在内存中发生变化后,寄存器中的数据没有发生变化,所以打印的是寄存器中的数据。解决这一问题,就使用volatile关键词,那么对a的存取都是直接对内存做操作的。

static_cast运算符

它的语法是:

static_cast<type-name>(expression)

仅当type-name可被隐式转换成expression所属的类型或者expression可以隐式转换成type-name类型时,上述转换才合法,否则出现bad_cast异常。

例如,枚举值可以隐式转换成整型,那么整型就可以通过static_cast转换成枚举型。

总之,static_cast只允许"相似"的类型间的转换,而不允许"差异很大"的类间的转换。

reinterpret_cast运算符:重新诠释类型,进行疯狂的类型转换

它的语法时:

reinterpret_cast<type-name>(expression)

它可以进行类型间"不可思议"的互换,但是它不允许删除const,也不允许进行丧失数据的转换

例如下面这两个例子:

#include<iostream>
int main()
{
    using namespace std;
    struct dat {short a; short b;};
    long value =0xA224B118;
    dat *pd=reinterpret_cast<dat*>(&value);
    cout<<hex<<pd->a;
}
#include<iostream>
void fun()
{
    std::cout<<"hello world!\n";
}
int main()
{
    using namespace std;
    void (*foo)();
    foo=fun;
    int* p=reinterpret_cast<int*>(foo);
}

通常,reinterpret_cast转换符适用于底层编程技术,是不可移植的,因为不同系统可能使用不同大小,不同顺序来存储同一类型的数据。

总之,C语言类型转换比reinterpret_cast还要"疯狂",非常不安全,所以推荐使用cast运算符来实现类型转换。

到此这篇关于C++超详细讲解RTTI和cast运算符的使用的文章就介绍到这了,更多相关C++ RTTI和cast内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • c++中的const_cast用法大全

    const_cast是一种C++运算符,主要是用来去除复合类型中const和volatile属性(没有真正去除). const_cast:作用: 指针或引用的转换符,用来移除变量的const或volatile限定符. 先来看c中不严谨的地方: const int ca = 30;  int* q = &ca;//C中是可以编译的,最多会得到一个warning,随后就可以对ca里的数据进行操作了. 疑问:那const几乎是失效的. 在c++编译中就会报错: error: invalid conve

  • C++ 中RTTI的使用方法详解

    C++ 中RTTI的使用方法详解 RTTI是运行阶段类型识别(Runtime Type Identification)的简称.这是新添加到c++中的特性之一,很多老式实现不支持.另一些实现可能包含开关RTTI的编译器设置.RTTI旨在为程序在运行阶段确定对象类型提供一种标准方式.很多类库已经成为其父类对象提供了实现这种方式的功能.但由于c++内部并不支持,因此各个厂商的机制通常互不兼容.创建一种RTTI语言标准将使得未来的库能够彼此兼容. c++有3个支持RTTI的元素 如果可能的话,dynam

  • C++中4种类型转换方式 cast操作详解

    Q:什么是C风格转换?什么是static_cast,dynamic_cast以及reinterpret_cast?区别是什么?为什么要注意? A:转换的含义是通过改变一个变量的类型为别的类型从而改变该变量的表示方式.为了类型转换一个简单对象为另一个对象你会使用传统的类型转换操作符.比如,为了转换一个类型为doubole的浮点数的指针到整型:代码:inti;doubled; i=(int)d;或者: i=int(d); 对于具有标准定义转换的简单类型而言工作的很好.然而,这样的转换符也能不分皂白的

  • C++ RTTI与4种类型转换的深入理解

    前言 RTTI 是 Run Time Type Information 的缩写,从字面上来理解就是执行时期的类型信息,其重要作用就是动态判别执行时期的类型. 并不是说这篇文章是RTTI,和用于RTTI的四种类型转换,而是介绍RTTI,再介绍一下4种类型转换,因为RTTI有用到其中一种类型转换,所以相当于两篇文章写在一起. 实际上 RTTI 用到的是typeid() 和 dynamic_cast(). 为什么会有RTTI? C++是一种静态类型语言,其数据类型是在编译期就确定的,不能在运行时更改.

  • C++四种cast使用详细介绍

    目录 一.static_cast 1.基本数据类型转换 2.指针和void指针的转换 3.父类和子类之间的转换 二.dynamic_cast 三.const_cast 1.加上const 2.去掉const 四.reinterpret_cast 在C++中,我们经常使用到类型的转换,像把一个int类型转换成char类型,一个int类型转换成double类型,这些转换属于隐式类型转换.而今天我们要来讲的是显式类型转换.C++提供了四种显式类型转换,分别是:static_cast.dynamic_c

  • C++中的RTTI机制详解

    前言 RTTI是"Runtime Type Information"的缩写,意思是运行时类型信息,它提供了运行时确定对象类型的方法.RTTI并不是什么新的东西,很早就有了这个技术,但是,在实际应用中使用的比较少而已.而我这里就是对RTTI进行总结,今天我没有用到,并不代表这个东西没用.学无止境,先从typeid函数开始讲起. typeid函数 typeid的主要作用就是让用户知道当前的变量是什么类型的,比如以下代码: 复制代码 代码如下: #include <iostream&g

  • C++超详细讲解RTTI和cast运算符的使用

    目录 1. RTTI 1.1 dynamic_cast运算符 1.2 typeid运算符 2. cast运算符 1. RTTI RTTI是运行阶段类型识别(Running Type Identificarion)的简称. 如何知道指针指向的是哪种对象? 这是个很常见的问题,由于我们允许使用基类指针指向派生类,所以基类指针指向的对象可能是基类对象,也可能是派生类对象.但是我们需要知道对象种类,因为我们需要使用正确的类方法. RTTI能解决上述问题. 1.1 dynamic_cast运算符 dyna

  • C++超详细讲解运算符重载

    目录 概念 赋值运算符重载 const成员 取地址及const取地址操作符重载 概念 C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似. 函数名字为:关键字operator后面接需要重载的运算符符号. 函数原型:返回值类型 operator操作符(参数列表) 需要注意的几点: 不能通过连接其他符号来创建新的操作符:比如operator@,必须是已有的操作符: 重载操作符必须有一个类类型

  • Python海象运算符超详细讲解

    目录 介绍 语法 用法 if 语句 while 循环 while 循环逐行读取文件 while 循环验证输入 推导式 三元表达式 总结 介绍 海象运算符,即 := ,在 PEP 572 中被提出,并在 Python3.8 版本中发布. 海象运算符的英文原名叫Assignment Expresions,即赋值表达式. 它由一个冒号:和一个等号=组成,即:=.而它被称作walrus operator(海象运算符),是因为它长得像一只海象. 语法 海象运算符的语法格式如下: variable_name

  • C++ Boost Conversion超详细讲解

    目录 一.说明 二.示例和代码 三.更多示例代码 一.说明 Boost.Conversion 在头文件 boost/cast.hpp 中定义了转换运算符 boost::polymorphic_cast 和 boost::polymorphic_downcast.它们旨在更精确地处理类型转换——通常使用 dynamic_cast 完成. 库由两个文件组成.分别在boost/cast.hpp文件中定义了boost::polymorphic_cast和boost::polymorphic_downca

  • C语言操作符超详细讲解下篇

    目录 前言 赋值操作符 单目操作符 单目操作符介绍 sizeof 和 数组 关系操作符 逻辑操作符 条件操作符 逗号表达式 下标引用与函数调用和结构成员 [ ] 下标引用操作符 ( ) 函数调用操作符 访问一个结构的成员 表达式求值 隐式类型转换-整形提升 算术转换 操作符的属性 总结 前言 本文接着学习操作符的内容. 赋值操作符 赋值操作符就是能够重新赋值 int weight = 120;//体重 weight = 89;//不满意就赋值 double salary = 10000.0; s

  • C语言操作符超详细讲解上篇

    目录 前言 1.操作符的分类 2.算术操作符 3.移位操作符 3.1 左移操作符 3.1.1 正数左移1位 3.1.2 负数左移1位 3.2 右移操作符 3.2.1 正数右移1位 3.2.2 负数右移1位 3.3 移位操作符说明 4.位操作符 4.1 练习 1 4.2 练习 2 总结 前言 操作符主要内容包括:各种操作符的介绍,用表达式求值. 1.操作符的分类 算术操作符 移位操作符 位操作符 赋值操作符 单目操作符 关系操作符 逻辑操作符 条件操作符 逗号表达式 下标引用.函数调用和结构成员

  • C语言超详细讲解指针的概念与使用

    目录 一.指针与一维数组 1. 指针与数组基础 2. 指针与数组 3. 一个思考 二.指针与字符串 三.指针和二维数组 1. 指针数组与数组指针 2. 指针数组 3. 数组指针 一.指针与一维数组 1. 指针与数组基础 先说明几点干货: 1. 数组是变量的集合,并且数组中的多个变量在内存空间上是连续存储的. 2. 数组名是数组的入口地址,同时也是首元素的地址,数组名是一个地址常量,不能更改. 3. 数组的指针是指数组在内存中的起始地址,数组元素的地址是指数组元素在内存中的其实地址. 对于第一点数

  • Java超详细讲解三大特性之一的多态

    目录 多态性 instanceof 关键字的使用 ==和equals()区别 object类中toString()的使用 static关键字的使用 总结 多态性 1理解多态性:可以理解为一个事物的多种形态. 2何为多态性:对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用) 3多态的使用:虚拟方法调用,有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法,多态性的使用前提:类的继承关系,方法的重写,总结:编译,看左边,运行,

  • C++超详细讲解强制类型转换的用法

    目录 static_cast dynamic_cast const_cast reinterpret_cast static_cast static_cast<type-id>(expression) 将 expression 转换为 type-id 类型.static_cast 是静态类型转换,发生在编译期.这种转换不会进行运行时的动态检查(RTTI),因而这种转换可能是不安全的.static_cast 典型应用场景如下: 1. 类的层级结构中,基类和子类之间指针或者引用的转换. 上行转换(

  • C++ primer超详细讲解关联容器

    目录 使用关联容器 关联容器概述 定义关联容器 pair类型 关联容器操作 关联容器迭代器 添加元素 删除元素 map的下标操作 访问元素 允许重复关键字的容器的名字开中都有包含单词multi,不保持关键字按顺序存储的容器的名字都以unordered开头. 使用关联容器 (1)经典的map程序 单词计数程序 map<string, size_t>word_count; string word; while (cin >> word) ++word_count[word]; for

随机推荐