author:d.schmidt@vanderbilt.edu Vanderbilt University
动态绑定的动机
人们遇到2种情况:知道想要什么样的类接口,但是不知道最合适的表述形式;或者知道想要什么样的算法,但是不知道特定操作如何被实现。
于是,人们自然地想到,放置某种形式的抽象占位(place-holder)或许是合适的.
动态绑定不比函数指针更强大,但比它更好理解,更不容易出错.
动态绑定允许程序通过一个基类指针调用通用方法:
class Base{ public: virtual int vf(void);};
Base *bp = /* pointer to a subclass */
bp->vf();
然而,运行时,这个调用会调用子类的特化的方法.
class Derived: public Base{ public: virtual int vf(void);};
Derived d;
bp=&d;
bp->vf(); //invoke Derived::vf()
在C++,这要求通用和特化方法都是虚拟的.
动态绑定带来更好的Flexibility和Extensibility.
- Flexibility='easily recombine existing components into new configurations'
- Extensibility='easily add new components'
动态绑定VS静态绑定
继承的回顾
子类的指针总是可以被用作指向公开继承的基类的指针. Caveat(警告):这种转换可能是不合法的,不安全的.
这时是调用那个方法呢,依赖于绑定的类型.
什么时候动态绑定?什么时候竟态绑定?
采用静态绑定:
[1]当你确信下一级派生类不会动态地改写这个操作(只是重新定义,隐藏)
[2]尽可能地重用具体的数据类型
采用动态绑定:
[1]派生类运行时可能会采用新的方法实现
[2]用于构建动态类型架构
Effeciency和Flexibility是选择时需要考虑的因素.
采用静态绑定,更有效率;动态绑定更灵活.但动态绑定对象难于保存在共享内存中.
C++中,动态绑定通过virtual关键字来标记.
ABC: Abastract Base Class
PVC: Pure Virtual Class
抽象的析构函数的唯一作用是导致该类被定义成ABC--抽象基类。但不会迫使派生类的析构函数也变成抽象的。
在C++中,子类的构造函数会自动调用父类的构造函数。同样,子类的析构函数也会自动调用父类的析构函数。
调用指针指向的方法的机制是什么?
在C++中,就是静态绑定和Virtual Method Tables。在C中,使Method Dispatch Tables。
对象指针有个暗藏的指针属性vptr*
Downcasting是有危险的!
而Upcasting总是OK的。
static_cast其实就是C中的强制类型转换
RTTI(Run Time Type Identification)
RTTI只支持动态绑定的类.RTTI是一种技术,允许程序在运行时查询对象的类型。
RTTI的用法
if(Derived *d=dynamic_cast&b){
// do
}
else{
// error
}
dynamic_cast引用失败会报出bad_cast 异常!
try{
Derived &d = dynamic_castobj;
}catch(bad_cast){
/*... */
}
陪伴着dynamic_cast<>, C++还提供了typeid()这样一个操作符
typeid(type) yields const Type_info&
typeid(expr) yields const Type_info&
RTTI的例子
Base *bp=new Derived;
Base &br=*bp;
typeid(bp) == typeid(Base*) //true
typeid(bp) == typeid(Derived*) //false
typeid(*bp)== typeid(Base) //false
typeid(*bp)== typeid(Derived) //true
typeid(br) == typeid(Base &) //false
typeid(br) == typeid(Derived&) //true
typeid(&br)== typeid(Base *) //true
typeid(&br)== typeid(Derived*) //false
注意,如果比较的是对象的话,参照的实际对象;
反之,如果比较的知识指针,还是参照声明来。
阅读(597) | 评论(0) | 转发(0) |