C++ Primer 学习笔记_36_面向对象编程(七)-虚函数与多态(四):RTTI、dynamic_cast、typeid与显示转换、类与类之间的关系uml
在上一篇有提到RTTI,本文将详细分析一下RTTI。
一、RTTI、dynamic_cast、typeid与显示转换
通过运行时类型识别(Run-time type information (RTTI)),程序能够使用基类的指针或引用来检索这些指针或引用所指对象的实际类型。
运行时类型识别主要包含三个元素:
-
dynamic_cast操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用。
-
typeid操作符,返回指针或引用所指对象的实际类型。
-
The type_info class.
Used to hold the type information returned by the typeid operator.
typeid表达形式如:typeid(e)。这里e是任意表达式或类型名。
Base *bp; Derived *dp; if(typeid(*bp) == typeid(*dp)) {...} if(typeid(*bp) == typeid(Derived) {...}
第一个if中,比较bp所指对象与dp所指对象的实际类型,如果他们指向同一类型,则测试成功。类似地,如果bp当前指向Derived对象,则第二个if成功。
为了支持RTTI,为每一个多态类创建一个type_info 对象(静态数据区),并把其地址保存到vtable中的固定位置(一般为第一个位置)(取决于具体编译器实现,标准并没有规定)。
#include <iostream> using namespace std; class Shape { public: virtual void Draw() = 0; virtual ~Shape() {} }; class Circle : public Shape { public: void Draw() { cout << "Circle Draw ..." << endl; } }; class Square : public Shape { public: void Draw() { cout << "Square Draw ..." << endl; } }; int main(void) { Shape *p; Circle c; p = &c; p->Draw(); //使用dynamic_cast 的条件 //1、开启运行时类型信息;2、应用在具有多态关系的继承体系上; if (dynamic_cast<Circle *>(p)) { cout << "p is point to a Circle object" << endl; Circle *cp = dynamic_cast<Circle *>(p); // 安全向下转型 cp->Draw(); //效率没有 p->Draw(); 高 } else if (dynamic_cast<Square *>(p)) { cout << "p is point to a Square object" << endl; } else { cout << "p is point to a Other object" << endl; } cout << typeid(*p).name() << endl; cout << typeid(Circle).name() << endl; if (typeid(Circle).name() == typeid(*p).name()) { cout << "p is point to a Circle object" << endl; ((Circle *)p)->Draw(); } else if (typeid(Square).name() == typeid(*p).name()) { cout << "p is point to a Circle object" << endl; ((Square *)p)->Draw(); } else { cout << "p is point to a Other object" << endl; } return 0; }
运行结果(在VS2012中运行,其他编译器输出结果有所不同):
如上所述,dynamic_cast 和 typeid 操作符 都可以实现运行时类型识别。其中使用dynamic_cast 时需要开启运行时类型信息,在项目-》属性-》C/C++-》语言-》启用运行时类型信息。在使用typeid时需要注意的是返回的是type_info 对象的引用,且type_info 类的拷贝构造函数和赋值运算符都声明为私有,故不能这样写: type_info tf = typeid(Circle);
cast-name<type>(expression);
int *a; char *b = (char*) a; //圆括号效果与使用reinterpret_cast相同 char *b = reinterpret_cast<char*>(ip);
1、依赖关系(Dependence)
依赖关系(Dependence):假设A类的变化引起了B类的变化,则说名B类依赖于A类。
public class Driver { public void drive(Car car) { car.move(); } …… } public class Car { public void move() { ...... } …… }
依赖关系有如下三种情况:
(1)、A类是B类中的(某中方法的)局部变量;
(2)、A类是B类方法当中的一个参数;
(3)、A类向B类发送消息,从而影响B类发生变化;
2、泛化关系(Generalization)
泛化关系(Generalization):A是B和C的父类,B,C具有公共类(父类)A,说明A是B,C的一般化(概括,也称泛化)
public class Person { protected String name; protected int age; public void move() { …… } public void say() { …… } } public class Student extends Person { private String studentNo; public void study() { …… } }
在UML当中,对泛化关系有三个要求:
(1)、子类与父类应该完全一致,父类所具有的属性、操作,子类应该都有;
(2)、子类中除了与父类一致的信息以外,还包括额外的信息;
(3)、可以使用父类的实例的地方,也可以使用子类的实例;
3、关联关系(Association)
关联关系(Association):类之间的联系,如客户和订单,每个订单对应特定的客户,每个客户对应一些特定的订单,再如篮球队员与球队之间的关联(下图所示)。
public class Customer { private Product[] products; …… } public class Product { private Customer customer; …… }
public class Customer { private Address address; …… } public class Address { …… }
public class Node { private Node subNode; …… }
表示方式 |
多重性说明 |
1..1 |
表示另一个类的一个对象只与一个该类对象有关系 |
0..* |
表示另一个类的一个对象与零个或多个该类对象有关系 |
1..* |
表示另一个类的一个对象与一个或多个该类对象有关系 |
0..1 |
表示另一个类的一个对象没有或只与一个该类对象有关系 |
m..n |
表示另一个类的一个对象与最少m、最多n个该类对象有关系 (m<=n) |
public class Form { private Button buttons[]; …… } public class Button { … }
4、聚合关系(Aggregation)
聚合关系(Aggregation):表示的是整体和部分的关系,整体与部分 可以分开.
public class Car { private Engine engine; public Car(Engine engine) { this.engine = engine; } public void setEngine(Engine engine) { this.engine = engine; } …… } public class Engine { …… }
如:电话机包括一个话筒
电脑包括键盘、显示器,一台电脑可以和多个键盘、多个显示器搭配,确定键盘和显示器是可以和主机分开的,主机可以选择其他的键盘、显示器组成电脑;
5、组合关系(Composition)
组合关系(Composition):也是整体与部分的关系,但是整体与部分不可以分开.
public class Head { private Mouth mouth; public Head() { mouth = new Mouth(); } …… } public class Mouth { …… }
如:公司和部门,部门是部分,公司是整体,公司A的财务部不可能和公司B的财务部对换,就是说,公司A不能和自己的财务部分开; 人与人的心脏.
比较5种关系:
继承体现的是类与类之间的纵向关系,其他4种体现的是类与类之间的横向关系。
关联强弱
依赖<关联<聚合<组合继承(A is B)
关联、聚合、组合(A has B)
依赖(A use B)
6、实现关系(Implementation)
实现关系(Implementation):是用来规定接口和实线接口的类或者构建结构的关系,接口是操作的集合,而这些操作就用于规定类或者构建的一种服务。
public interface Vehicle { public void move(); } public class Ship implements Vehicle { public void move() { …… } } public class Car implements Vehicle { public void move() { …… } }
参考:
C++ primer 第四版
C++ primer 第五版
版权声明:本文为博主原创文章,未经博主允许不得转载。