C++之动态类型识别

动态类型

问题:下面的程序有问题吗?

class Parent{
public:
	virtual ~Parent(){
        
	}
};

class Child: public Parent{
    
};

void test(Parent* p){
    Child* c = (Child*)p; //?
}

由于基类指针可以直接指向派生类对象(C++赋值兼容性原则),因此可能存在指针所指类型与具体指向的对象类型不同的情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1N2qK592-1583289348697)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583220109992.png)]

动态类型指的是基类指针所指向的对象的实际类型

void test(Parent* p){
    Child* c = (Child*)p; //当p的动态类型为Child时,转换成功;
    					  //否则,可能出现无法预知的错误
}

基类指针是否可以强制类型转换为子类指针取决于动态类型!

C++中如何得到动态类型?

动态类型识别

C++中的多态根据实际的对象类型调用对应的虚函数
可以在基类中定义虚函数返回具体的类型信息
所有的派生类都必须实现类型相关的虚函数
每个类中的类型虚函数都需要不同的实现

利用多态进行动态类型识别

class Parent{
public:
	enum { ID = 0 };
	
	virtual int type(){
        return ID;
	}	
};

class Child : public Parent{
public:
	enum { ID = 1 };
	
	int type(){
        return ID;
	}
	
	int add(int a, int b){
        return a+b;
	}
};

void test(Parent* p){
	if(p->type() == Child::ID ){
        Child* c = (Child*)p;
        
        cout << c->add(3, 5)<<endl;
	}
}

完整代码

#include <cstdlib>
#include <iostream>

using namespace std;

class Parent
{
public:
    enum { ID = 0 };
    
    virtual int type()
    {
        return ID;
    }
};

class Child : public Parent
{
public:
    enum { ID = 1 };
    
    int type()
    {
        return ID;
    }
    
    int add(int a, int b)
    {
        return a + b;
    }
};

void test(Parent* p)
{
    if( p->type() == Child::ID )
    {
        Child* c = (Child*)p;
        
        cout<<"Dynamic Type: "<<"Child"<<endl;
        cout<<"add: "<<c->add(2, 3)<<endl;
    }
    
    if( p->type() == Parent::ID )
    {
        cout<<"Dynamic Type: "<<"Parent"<<endl;
    }
}

int main(int argc, char *argv[])
{
    Parent parent;
    Child child;
    
    test(&parent);
    test(&child);
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zK7VIPeO-1583289348701)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583225087584.png)]

使用虚函数进行动态类型识别的缺陷
必须从基类开始提供类型虚函数
所有的派生类都必须重写类型虚函数
每个派生类的类型ID必须唯一

tips:

利用虚函数进行动态类型识别的方法可以满足工程的需要,但是维护性会随着派生类的增多而成指数级增加。

新的关键字dynamic_cast

新的关键字dynamic_cast
dynamic_cast是C++中的新型转换关键字
dynamic_cast主要用于基类和派生类之间的转换
dynamic_cast要求使用的目标对象类型是多态的
即:所在类族至少有一个虚函数
用于指针转换时,转换失败返回空指针
用于引用转换时,转换失败将引发bad_cast异常

利用dynamic_cast进行动态类型识别

class Parent
{
public:
    virtual ~Parent() //dynamic_cast要求 所在类族至少有一个虚函数
    {
    }
};

class Child : public Parent
{
public:
    int add(int a, int b)
    {
        return a + b;
    }
};

void test(Parent* p){
    Child* c = dynamic_cast<Child*>(p);
    
    if(c != NULL){ //转换成功
        cout << c->add(3,5)<<endl;
    }
}

完整代码

#include <cstdlib>
#include <iostream>

using namespace std;

class Parent
{
public:
    virtual ~Parent() //
    {
    }
};

class Child : public Parent
{
public:
    int add(int a, int b)
    {
        return a + b;
    }
};

class NewChild : public Parent 
{
};

void test(Parent* p)
{
    Child* c = dynamic_cast<Child*>(p);
    
    if( c != NULL ) //Child类
    {
        cout<<"Dynamic Type: "<<"Child"<<endl;
        cout<<"add: "<<c->add(2, 3)<<endl;
    }
    else
    {
        if( dynamic_cast<NewChild*>(p) != NULL ) //NewChild类
        {
            cout<<"Dynamic Type: "<<"NewChild"<<endl;
        }
        else //Parent类
        {
            cout<<"Dynamic Type: "<<"Parent"<<endl;
        }
    }
}

int main(int argc, char *argv[])
{
    Parent parent;
    Child child;
    NewChild nc;
    
    test(&parent);
    test(&child);
    test(&nc);
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

在这里插入图片描述

dynamic_cast的优势
不用显示的声明和定义类型虚函数
不用为类族中的每个类分配类型ID
dynamic_cast的缺陷
只能用于具有虚函数的类族

tips:

使用dynamic_cast进行动态类型识别可以取代类型虚函数的方案,但是在本质上dynamic_cast还是需要类族中存在虚函数(在工程上常把析构函数作为这个虚函数定义)。

C++中是否可以动态得到任意变量的类型信息呢?

C++提供了typeid关键字用于动态获取类型信息
typeid关键字返回对应参数的类型信息
typeid返回一个type_info类对象
当typeid的参数为NULL时,抛出bad_typeid异常
type_info类的使用需要包含 < typeinfo >

type_info类的实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qkXsM2nn-1583289348703)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583284998350.png)]

typeid关键字的使用

Parent p;
Child c;
int index;
char ch;

const type_info& tip = typeid(p); //typeid关键字
const type_info& tic = typeid(c);
const type_info& tii = typeid(index);
const type_info& tich = typeid(ch);

cout << tip.name()<<endl; //注意:name()函数返回的是Parent类型在gcc编译器内部的类型名字 
						  //strcmp(tip.name(), "Parent")写法错误
cout << tic.name()<<endl;
cout << tii.name()<<endl;
cout << tich.name()<<endl;

cout << (typeid(p) == typeid(Parent) ) <<endl; //比较方式 重载运算符==
cout << (typeid(c) == typeid(Child) ) <<endl;
cout << (typeid(index) == typeid(int) ) <<endl;
cout << (typeid(ch) == typeid(char) ) <<endl;

完整代码

#include <cstdlib>
#include <iostream>
#include <typeinfo>

using namespace std;

class Parent
{
public:
    virtual ~Parent()
    {
    }
};

class Child : public Parent
{
public:
    int add(int a, int b)
    {
        return a + b;
    }
};

class NewChild : public Parent 
{
};

void test(Parent* p)
{
    if( typeid(*p) == typeid(Child) ) //具体使用方式 Child
    {
        Child* c = dynamic_cast<Child*>(p);
    
        cout<<"Dynamic Type: "<<"Child"<<endl;
        cout<<"add: "<<c->add(2, 3)<<endl;
    } 
    else if( typeid(*p) == typeid(NewChild) ) //NewChild
    {
        cout<<"Dynamic Type: "<<"NewChild"<<endl;
    } 
    else if( typeid(*p) == typeid(Parent) ) //Parent
    {
        cout<<"Dynamic Type: "<<"Parent"<<endl;
    }

}

int main(int argc, char *argv[])
{
    Parent parent;
    Child child;
    NewChild nc;
    int index;
    char ch;
    
    const type_info& tp = typeid(parent);
    const type_info& tc = typeid(child);
    const type_info& tn = typeid(nc);
    const type_info& ti = typeid(index);
    const type_info& tch = typeid(ch);
    
    cout<<tp.name()<<endl;
    cout<<tc.name()<<endl;
    cout<<tn.name()<<endl;
    cout<<ti.name()<<endl;
    cout<<tch.name()<<endl;
    
    test(&parent);
    test(&child);
    test(&nc);
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p3HN4qop-1583289348705)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583288811719.png)]

分析:6Parent为Parent在g++编译器中的类型名

小结

C++中可以通过多态的方式进行动态类型识别
dynamic_cast关键字是可用于动态类型识别
typeid关键字在C++中专用于动态类型的识别

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值