第27课 - 动态类型识别
一.动态类型
1.1 由于基类指针可以直接指向派生类对象,因此可能存在指针所指类型与具体指向对象类型不同的情况
Parent* p = new Child();
1.2 动态类型指的是基类指针所指向对象的实际类型
void test(Parent* p)
{
/* 当p的动态类型为Child时,转换成功 */
/* 否则,可能出现无法预知的错误 */
Child* c = (Child*)p;
}
基类指针是否可以强制类型转换为子类指针取决于动态类型!
问题: 如何得到一个指针的动态类型??
二.动态类型识别的方法
2.1 C++中的多态根据实际对象类型调用对应的虚函数
2.1.1 可以在基类中定义虚函数返回具体的类型信息
2.1.2 所有的派生类都必须实现类型相关的虚函数
2.1.3 每个类中的类型虚函数都需要不同的实现
Source Example 2.1:
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
class Parent {
public:
enum {
ID = 0
};
virtual int GetType()
{
return ID;
}
};
class Child : public Parent {
public:
enum {
ID = 1
};
virtual int GetType()
{
return ID;
}
int Add (int a, int b)
{
return a + b;
}
};
void test (Parent *p)
{
if (p->GetType() == Child :: ID)
{
Child* c = (Child*)p;
cout<<c->Add(2,3)<<endl;
}
if (p->GetType() == Parent :: ID)
{
cout<<"Parent Type"<<endl;
}
}
int main(int argc, char** argv) {
Child c;
Parent p;
test(&c);
test(&p);
return 0;
}
输出结果如下:
2.1.4 使用虚函数进行动态类型识别的缺陷
a.必须从基类开始提供类型虚函数
b.所有的派生类都必须重写类型虚函数
c.每个派生类的ID都必须唯一
利用虚函数进行动态类型识别的方法可以满足工程的需要,但是维护性会随着派生类的增多而成指数型增加。
解决方案: 将ID替换为类的名称
Source Example 2.1.4:
#include <iostream>
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
class Parent {
public:
virtual const char * GetType()
{
return "Parent";
}
};
class Child : public Parent {
public:
virtual const char * GetType()
{
return "Child";
}
int Add (int a, int b)
{
return a + b;
}
};
void test (Parent *p)
{
if (strcmp (p->GetType(), "Child") == 0)
{
Child* c = (Child*)p;
cout<<c->Add(2,3)<<endl;
}
if (strcmp (p->GetType(), "Parent") == 0)
{
cout<<"Parent Type"<<endl;
}
}
int main(int argc, char** argv) {
Child c;
Parent p;
test(&c);
test(&p);
return 0;
}
方案缺点: 进行字符串的比较,程序效率会降低很多!
三.新的关键字dynamic_cast
3.1 dynamic_cast 是C++中新型转换关键字
3.2 dynamic_cast 主要用于基类和派生类之间的转换
3.3 dynamic_cast要求使用目标对象类型是多态的
3.3.1 即:所在类族至少有一个虚函数(在工程中一般将析构函数定义为析构函数)
3.3.2 用于指针转换时,转换失败返回空指针
3.3.3 用于引用转换时,转换失败将引发bad_cast异常
Source Example 3:
#include <iostream>
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
class Parent {
public:
/* 必须存在一个虚函数,在工程中一般将析构函数定义为析构函数 */
virtual ~Parent()
{
}
};
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<<"Dynamic Type:"<<"Child"<<endl;
cout<<"add:"<<c->Add(2,3)<<endl;
}
else
{
cout<<"Dynamic Type:"<<"Parent"<<endl;
}
}
int main(int argc, char** argv) {
Child c;
Parent p;
test(&c);
test(&p);
return 0;
}
输出结果如下:
3.4 dynamic_cast 的优势
3.4.1 不用显示的声明和定义类型虚函数
3.4.2 不用为类族中的每个类分配ID
3.5 dynamic_cast 的缺陷
3.5.1 只能用于具有虚函数的类族
使用dynamic_cast进行动态识别可以取代类型虚函数的方案,但是在本质上dynamic还是需要类族中存在虚函数.
(在工程上常把析构函数作为这个虚函数定义)
四.C++提供了typeid关键字用于动态获取类型信息
4.1 typeid 关键字返回对应参数的类型信息
4.2 typeid 返回一个type_info类对象
4.2.1 当typeid的参数为NULL时,抛出bad_typeid异常
4.3 type_info类的使用需要包含<typeinfo>
Source Example 4(typeid关键字的使用):
#include <iostream>
#include <string.h>
#include <typeinfo>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
class Parent {
public:
virtual ~Parent()
{
}
};
class Child : public Parent {
public:
int Add (int a, int b)
{
return a + b;
}
};
void test(Parent *p)
{ /* typeid的参数可以是具体的对象,也可以是类型 */
if ( typeid (*p) == typeid(Child))
{
Child *c = dynamic_cast<Child*>(p);
cout<<"Dynamic Type:"<<"Child"<<endl;
cout<<"add:"<<c->Add(2,3)<<endl;
}
else
{
cout<<"Dynamic Type:"<<"Parent"<<endl;
}
}
int main(int argc, char** argv) {
int index;
char ch;
Child c;
Parent p;
const type_info& tp = typeid(Parent);
const type_info& ti = typeid(index);
const type_info& tc = typeid(Child);
const type_info& tch = typeid(ch);
/* 输出6Parent,这个类型名字是在GCC内部系统里面的 */
/* 注意,不要用这个名字来些代码,例如strcmp(tp.name(), "Parent") == 0 */
cout<<tp.name()<<endl;
/* 输出i */
cout<<ti.name()<<endl;
/* 输出5Child */
cout<<tc.name()<<endl;
/* 输出c */
cout<<tch.name()<<endl;
test(&p);
test(&c);
return 0;
}
输出结果如下: