RTTI(dynamic_cast与typeid)

RTTI 运行时类型识别,程序能够使用基类的指针或引用来检索所指对象的实际派生类型
C++通过两个操作符提供RTTI
1.dynamic_cast 操作符,将基类的指针或引用安全的转换为派生类的指针或引用

2.typeid操作符,返回指针或引用所指对象的实际类型


dynamic_cast:
使用dynamic_cast模拟虚函数调用

请看下面这个小程序

#include <iostream>
using namespace std;

class A
{
public:
	void p() { cout<<"AAAAA"<<endl;}
	virtual void show() { cout<<"aaaaaaaaaa"<<endl;}
};

class B:public A
{
public:
	void p() { cout<<"BBBBB"<<endl;}
	void show() { cout<<"bbbbbbbbbb"<<endl;}
};

class C:public A
{
public:
	void p() { cout<<"CCCCC"<<endl;}
	void show() { cout<<"ccccccccccc"<<endl;}
};

class Factory
{
public:
	void show(A *a) 
	{ 
	if(B *b=dynamic_cast<B*>(a))
		b->p();
	else if(C *c=dynamic_cast<C*>(a))
		c->p();
	else
		cout<<"base class...";
	}
};

int main()
{
Factory *f=new Factory;
f->show(new C);
cout<<endl;
}

上面这个程序很简单,类B,C从类A继承,类A有个虚函数void show(),请注意void p()不是虚函数
f->show(new C) ->"CCCCC"
dynamic_cast=>实际上就是把基类的指针安全的转化为这个指针实际所指对象.
它执行两个操作,首先判断能否转化,也就是说看看你给我的是不是你想转换的对象,如果不能转化就失败。
这时,如果是转化到指针失败,返回0,如果转化到引用失败,则抛出bad_cast异常.如果可以转化则转换。


如上面,给f->show()的是指向C的指针,所以dynamic_cast<B*>(a),转换失败,返回0,执行dynamic_cast<C*>(a),
转换成功,输出“CCCCC”


这里有几点需要注意和思考:
1.class A必须至少有一个虚函数,即A必须是多态的.如果去掉A::void show()中的virtual,则报如下错误
cs.cc: 在成员函数‘void Factory::show(A*)’中:
cs.cc:38:28: 错误: 无法将‘a’从类型‘A*’动态转换到类型‘class B*’(源类型不是多态的)
cs.cc:40:33: 错误: 无法将‘a’从类型‘A*’动态转换到类型‘class C*’(源类型不是多态的)


2.有虚函数为什么还需要dynamic_cast?
在有些情况下是不可能使用虚函数的,如上,A::void p(),不是虚函数,这怎么办?
那改成虚函数不就好了吗?那如果改不了呢?比如使用第三方共享库,恰好就不是虚函数


3.那可不可以直接强制类型转换?
完全可以,但你会发现很多问题。
#1. A *a=new B; ((B*)a)->p(); //"BBBBB" (->优先级比类型转换高,所以需要加括号)
#2. A *a=new A; ((B*)a)->p(); //"BBBBB"
#3. A *a=new C; ((B*)a)->p(); //"BBBBB"
发现问题了没有,强制类型转换结果可能完全出乎意料,不是转换安全的。


4.RTTI解决虚函数解决不了的问题
如果我需要访问派生类独有的属性和方法,那么虚函数就无能为力了,这时候就是RTTI的天下了




typeid操作符:
typei表达式: typeid(c) ->>c是表示对象的任何表达式
返回结果是type_info的标准库类型的引用,type_info类提供==,!=操作,还有一成员函数name(),返回所表示的类型
名(不同系统产生结果不同)
如typeid(A)==typeid(B) ->false
typeid(A).name() ->"1A"


如重写上面 Factory::show() (使用typeid需要包含头文件typeinfo)

void show(A *a) 
	{ 
		if(typeid(*a)==typeid(B))
			((B*)a)->p();
		else if(typeid(*a)==typeid(C))
			((C*)a)->p();
		else
			cout<<"Bse class...";
	}

在这里我使用了强制类型转换,因为如果if为真,那么可以确定就是那个类型,所以还是转化安全的(个人意见)


这里需要注意几点:
#1.如果类A没有虚函数,那么void Factory::show()中typeid(*a),返回的将是操作数的静态类型,即typeid(*a)永远
等于typeid(A)


#2.如果main()中调用f->show(0);,那么如果A有一个虚函数,则typeid(*a)抛出一个bad_typeid异常,如果A没有一个虚函数,
则使用静态类型,则与#1同

<think>好的,我现在需要帮助用户理解这段代码:`auto log_type = dynamic_cast<t_gsetout *>(&gset)->log_type();`。首先,我得拆解每一部分的含义,并考虑用户可能的疑问。 首先,用户可能对`auto`关键字不熟悉,尤其是在C++11及以后的版本中,`auto`用于自动类型推断。这里`log_type`的类型由右边的表达式决定,所以需要先确定右边返回的类型是什么。 接下来是`dynamic_cast<t_gsetout *>(&gset)`。dynamic_cast通常用于在继承层次结构中进行安全的向下转型。这里将`gset`的地址转换为`t_gsetout`指针类型。这意味着`gset`可能是某个基类的对象,而`t_gsetout`是它的派生类,或者两者在多态继承链中。用户可能对dynamic_cast的使用场景和安全性有疑问,比如如果转换失败会返回nullptr,但代码中直接调用方法,可能存在风险,是否需要检查转换结果? 然后,转换后的指针直接调用`log_type()`方法。这里需要注意,如果`dynamic_cast`失败,返回的指针会是nullptr,这时候调用方法会导致未定义行为,比如程序崩溃。用户可能没有意识到这一点,所以需要提醒潜在的风险,或者确认代码是否在确保转换成功的情况下运行。 整体来看,用户可能想了解这段代码的作用,涉及的知识点包括auto类型推断、dynamic_cast的用法、多态和继承关系,以及可能的错误处理。此外,用户可能对代码中的类结构(如t_gsetout和gset的类型)不太清楚,但根据现有信息,只能基于代码本身进行分析。 需要分步骤解释每个部分,并指出可能的问题,比如未检查dynamic_cast的结果。同时,应该建议良好的编程实践,比如在使用前检查指针是否有效,或者考虑使用智能指针等。可能用户在实际开发中遇到了问题,或者想优化这段代码,所以需要给出改进建议。</think>### 代码解析:`auto log_type = dynamic_cast<t_gsetout *>(&gset)->log_type();` #### **代码含义** 1. **核心目标**:通过多态机制获取`gset`对象的`log_type`属性值 2. **具体步骤**: - `dynamic_cast<t_gsetout *>(&gset)`: 将`gset`对象转换为`t_gsetout*`类型的指针 - `->log_type()`: 调用转换后指针的成员函数`log_type()` - `auto log_type`: 自动推断`log_type()`返回值的类型 --- #### **涉及的核心知识点** ##### 1. **dynamic_cast 类型转换** - **作用**:用于具有继承关系的类之间的**运行时安全向下转型** - **关键特性**: - 要求基类必须有虚函数(多态类型) - 转换失败返回`nullptr`(指针类型)或抛出异常(引用类型) - 示例验证: ```cpp class Base { virtual void dummy() {} }; // 必须含虚函数 class Derived : public Base { /*...*/ }; Base* b = new Derived; Derived* d = dynamic_cast<Derived*>(b); // 安全向下转型 ``` ##### 2. **auto 类型推导** - **C++11 特性**:编译器自动推导变量类型 - **推导规则**: $$ \text{auto} \equiv \text{表达式返回值的类型} $$ - **验证实验**: ```cpp auto x = 3.14; // x → double auto y = std::string("hello"); // y → std::string ``` ##### 3. **类成员访问操作符`->`** - **作用**:通过指针访问对象成员 - **等价关系**: $$ ptr->member \equiv (*ptr).member $$ - **内存示意图**: ``` [ptr] → 0x1000 → [t_gsetout对象] ├─ member1 └─ log_type() ``` ##### 4. **多态继承关系(隐含知识)** - **必要前提**: - `gset`对象必须是`t_gsetout`的派生类实例 - `t_gsetout`类必须定义虚函数(否则`dynamic_cast`无法使用) --- #### **潜在风险改进建议** 1. **未检查转换结果**(严重问题): ```cpp // 原代码风险:若转换失败会引发空指针访问 t_gsetout* p = dynamic_cast<t_gsetout*>(&gset); if(p) { // 必须添加安全检查 auto log_type = p->log_type(); } ``` 2. **类型设计建议**: - 基类应定义虚接口: ```cpp class ISettable { public: virtual std::string log_type() = 0; // 纯虚函数 virtual ~ISettable() = default; }; ``` --- #### **代码等价形式展开** ```cpp // 分步拆解版本 t_gsetout* temp_ptr = dynamic_cast<t_gsetout*>(&gset); // 类型转换 std::string log_type = temp_ptr->log_type(); // 成员访问 // 使用类型别名更清晰的版本 using SettingsPtr = t_gsetout*; SettingsPtr validated_ptr = dynamic_cast<SettingsPtr>(&gset); if(validated_ptr) { auto log_type = validated_ptr->log_type(); } ``` --- ### **扩展学习建议** 1. **调试技巧**:在`dynamic_cast`后设置断点,观察指针值是否为`nullptr` 2. **类型安全对比**:尝试将`dynamic_cast`改为`static_cast`,观察编译器警告差异 3. **RTTI机制**:通过`typeid`运算符验证类型信息: ```cpp std::cout << typeid(gset).name(); // 输出gset的实际类型名称 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值