RTTI(运行时类型识别)
Run-Time Type Identification
- typeid < - > dynamic_cast
例子:
class Flyable
{
public:
virtual void takeoff() = 0;//起飞
virtual void land() = 0; //降落
};
class Bird:public Flyable
{
public:
void foraging(){}
virtual void takeoff(){}
virtual void land(){}
private:
//...
};
class Plane:public Flyable
{
public:
void carry(){}
virtual void takeoff(){}
virtual void land(){}
};
使用时:
void doSomething(Flyable *obj)
{
obj ->takeoff();
//如果是bird,则觅食
//如果是plane,则运输
obj -> land();
}
如果对指针能进行判断。然后根据传入指针不同调用不同方法。
void doSomething(Flyable *obj)
{
obj ->takeoff();
cout << typeid(*obj).name() <<endl;
if(typeid(*obj) == typeid(Bird))
{
Bird *bird = dynamic_cast<Bird *>(obj);
//尖括号内是我们想要转化成的类型。
bird -> foraging();
}
obj -> land();
}
总结:
dynamic_cast注意事项:
- 只能应用于指针和引用的转换
- 要转换的类型中必须包含虚函数
- 转换成功返回子类的地址,失败返回NULL
typeid注意事项:
-
type_id
返回一个type_info
对象的引用 - 如果想通过基类的指针获得派生类的数据类型,基类必须带有虚函数
- 只能获取对象的实际类型.(不能判断当前指针)
name()
& 运算符重载
RTTI代码示例
要求#ifndef FLYABLE_H
#define FLYABLE_H
class Flyable
{
public:
virtual void takeoff() = 0;//起飞
virtual void land() = 0; //降落
};
#endif
#ifndef PLANE_H
#define PLANE_H
#include <string>
#include "Flyable.h"
using namespace std;
class Plane :public Flyable
{
public:
void carry();
virtual void takeoff();
virtual void land();
};
#endif
#include <iostream>
#include "Plane.h"
using namespace std;
void Plane::carry()
{
cout << "Plane::carry()" << endl;
}
void Plane::takeoff()
{
cout << "Plane::takeoff()" << endl;
}
void Plane::land()
{
cout << "Plane::land()" << endl;
}
#ifndef BIRD_H
#define BIRD_H
#include "Flyable.h"
#include <string>
using namespace std;
class Bird :public Flyable
{
public:
void foraging();
virtual void takeoff();
virtual void land();
};
#endif // !BIRD_H
#include <iostream>
#include "Bird.h"
using namespace std;
void Bird::foraging()
{
cout << "Bird::foraging()" << endl;
}
void Bird::takeoff()
{
cout << " Bird::takeoff()" << endl;
}
void Bird::land()
{
cout << " Bird::land()" << endl;
}
main.cpp1:
#include <iostream>
#include "Bird.h"
#include "Plane.h"
using namespace std;
#include <stdlib.h>
void doSomething(Flyable *obj)
{
cout << typeid(*obj).name() << endl;
obj->takeoff();
if (typeid(*obj) == typeid(Bird))
{
Bird *bird = dynamic_cast<Bird *>(obj);
bird->foraging();
}
if (typeid(*obj) == typeid(Plane))
{
Plane *plane = dynamic_cast<Plane *>(obj);
plane->carry();
}
obj->land();
}
int main()
{
Bird b;
doSomething(&b);
system("pause");
return 0;
}
运行结果:
运行结果- 传入的是bird。所以执行了bird分支下的觅食。
- 当传入是plane时。执行carry。
代码说明typeid .dynamic_cast注意事项
int main()
{
int i =0;
cout <<typeid(i).name() << endl;
}
输出为int。打印出数据类型。
int main()
{
Flyable *p = new Bird();
cout << typeid(p).name() << endl;
cout << typeid(*p).name() << endl;
system("pause");
return 0;
}
p类型为Flyable *,*p类型为bird对象
将Flyable.h的两个纯虚函数改为普通的。
#ifndef FLYABLE_H
#define FLYABLE_H
class Flyable
{
public:
void takeoff(){ };//起飞
void land() {}; //降落
};
#endif
bird.h虚函数去掉。
#ifndef BIRD_H
#define BIRD_H
#include "Flyable.h"
#include <string>
using namespace std;
class Bird :public Flyable
{
public:
void foraging();
void takeoff();
void land();
};
#endif // !BIRD_H
bird 和flyable变成普通的继承。
int main()
{
// Flyable *p = new Bird();
// Bird *b = dynamic_cast<Bird *>p;
// //“dynamic_cast”:“Flyable”不是多态类型
//
system("pause");
return 0;
}
对于dynamic_cast.转换类型还是被转类型都要求有虚函数。
int main()
{
Flyable p;
Bird b = dynamic_cast<Bird>p;
//“dynamic_cast”:“Flyable”不是多态类型
system("pause");
return 0;
- 只能应用于指针和引用的转换
练习题
- 继承关系不是RTTI的充分条件,只是必要条件,所以存在继承关系的类不一定可以用RTTI技术
- RTTI的含义是运行时类型识别
- RTTI技术可以通过父类指针识别其所指向对象的真实数据类型
- 运行时类型别必须建立在虚函数的基础上,否则无需RTTI技术
巩固练习
定义一个能够移动(Movable)类,要求含有纯虚函数move
定义一个公交车(Bus)类,继承Movable类,并实现函数move,定义函数carry
定义一个坦克(Tank)类,继承Movable类,并实现函数move,定义函数shot。
定义函数doSomething(Movable *obj),根据s指向对象的类型调用相应的函数。
实例化公交车类和坦克类,将对象传入到doSomething函数中,调用相应函数
#include <iostream>
#include <stdlib.h>
#include <string>
#include <typeinfo>
using namespace std;
/**
* 定义移动类:Movable
* 纯虚函数:move
*/
class Movable
{
public:
virtual void move() = 0;
};
/**
* 定义公交车类:Bus
* 公有继承移动类
* 特有方法carry
*/
class Bus : public Movable
{
public:
virtual void move()
{
cout << "Bus -- move" << endl;
}
void carry()
{
cout << "Bus -- carry" << endl;
}
};
/**
* 定义坦克类:Tank
* 公有继承移动类
* 特有方法fire
*/
class Tank :public Movable
{
public:
virtual void move()
{
cout << "Tank -- move" << endl;
}
void fire()
{
cout << "Tank -- fire" << endl;
}
};
/**
* 定义函数doSomething含参数
* 使用dynamic_cast转换类型
*/
void doSomething(Movable *obj)
{
obj->move();
if(typeid(*obj) == typeid(Bus))
{
Bus *bus = dynamic_cast<Bus *>(obj);
bus->carry();
}
if(typeid(*obj) == typeid(Tank))
{
Tank *tank = dynamic_cast<Tank *>(obj);
tank->fire();
}
}
int main(void)
{
Bus b;
Tank t;
doSomething(&b);
doSomething(&t);
return 0;
}
运行结果:
运行结果异常处理
- 异常:程序运行期出现的错误。
- 异常处理:对有可能发生异常的地方做预见性的安排
网线- 内存不足。
关键字:
-
try...catch...
:尝试 捕获 - throw 抛出异常
思想:
主逻辑(try)与异常处理逻辑(catch)分开
异常处理三个函数f1,f2,f3.用f2调用f1,f3调用f2.
当f1出现异常会往上抛。如果f2可以处理就可以处理完成。
如果不能处理,会继续进行异常的传播。f3捕获并处理。
void fun1()
{
throw 1;
}
int main(){
try {
fun1();
}catch(int)//throw的是1,所以用int捕获
{
//.....
}
return 0;
}
try{
fun1();
}
catch(int)
{}
catch(double)
{}
catch)(...)//捕获所有的异常
{}
一个try可以有多个catch。不同异常做不同处理。
- 抛出值,捕获数据类型。
- 下面我们来做捕获值。
char getChar(const string& aStr,const int aIndex)
{
if (aIndex > aStr.size())
{
throw string("ivalid index!");
}
return aStr[aIndex];
}
string str("hello world");
cahr ch;
try{
ch = getChar(str,100);//这句抛异常,下句不会运行
cout << ch << endl;
}catch(string& aval){
cout << aval << endl;
}
常见的异常:
- 数组下标越界
- 除数为0
- 内存不足
异常处理与多态的关系:
异常处理&多态定义一个接口exception。多个子类来继承该类。那么我们可以通过父类对象捕获不同子类对象的异常。
void fun1()
{
throw new SizeErr();
}
void fun2()
{
throw new MemoryErr();
}
try{
fun1();
}catch(Exception &e)
{
e.xxx();
}
try{
fun2();
}catch(Exception &e)
{
e.xxx();
}
通过父类的引用,调用相应的子类处理函数。
异常处理代码示例
要求#include <iostream>
#include <stdlib.h>
#include "IndexException.h"
using namespace std;
void test()
{
throw 0.1;
}
int main(void)
{
try
{
test();
}
catch (double)
{
cout << "exception" << endl;
}
system("pause");
return 0;
}
- 1 对应 int
- 1.0 对应 double
catch (double &e)
{
cout << e << endl;
}
可以打印出抛出来的异常值:如0.1
#ifndef EXCEPTION_H
#define EXCEPTION_H
class Exception
{
public:
virtual void printException();
virtual ~Exception() {};
};
#endif
#include "Exception.h"
#include <iostream>
using namespace std;
void Exception::printException()
{
cout << " Exception::printException()" << endl;
}
#ifndef INDEX_EXCEPTION_H
#define INDEX_EXCEPTION_H
#include "Exception.h"
class IndexException:public Exception
{
public:
virtual void printException();
};
#endif
#include "IndexException.h"
#include <iostream>
using namespace std;
void IndexException::printException()
{
cout << "提示:下标越界" << endl;
}
#include <iostream>
#include <stdlib.h>
#include "IndexException.h"
using namespace std;
void test()
{
throw IndexException();
}
int main(void)
{
try
{
test();
}
catch (IndexException &e)
{
e.printException();
}
system("pause");
return 0;
}
运行结果:
运行结果int main(void)
{
try
{
test();
}
catch (Exception &e)
{
e.printException();
}
system("pause");
return 0;
}
依然打印出数组的提示。父类的引用可以使用到子类的处理函数。
int main(void)
{
try
{
test();
}
catch (...)
{
cout << "error" << endl;
}
system("pause");
return 0;
}
通过...可以捕获到所有异常。
练习题
- 在C++中异常处理通常使用try...catch...语法结构。
- 一个try语句可以对应一个或多个catch语句,但不能没有catch语句
- C++中使用throw抛出异常,通过catch捕获异常
巩固练习
函数division的两个参数为dividend(被除数)和divisor(除数)
要求用户输入除数和被除数,并作为参数传递给division函数
如果除数为0,则抛出异常,并被捕获,将异常的内容显示到屏幕上
#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;
/**
* 定义函数division
* 参数整型dividend、整型divisor
*/
int division(int dividend, int divisor)
{
if(0 == divisor)
{
// 抛出异常,字符串“除数不能为0”
throw string("除数不能为0");
}
else
{
return dividend / divisor;
}
}
int main(void)
{
int d1 = 0;
int d2 = 0;
int r = 0;
cin >> d1;
cin >> d2;
// 使用try...catch...捕获异常
try{
r = division(d1,d2);
cout << r << endl;
}catch(string &str){
cout << str <<endl;
}
return 0;
}
运行结果:
除数不能为0.
网友评论