继承: is a 基类A,派生类B,B is a A!
组合: is a part of 聚合(has a),关联(holds a)
动态特性:
绝大多数情况,程序的功能是在编译的时候确定下来,此为静态特性,若是运行时候确定下来,则是动态特性。
C++虚函数,抽象基类,动态绑定(Dynamic binding),多态(Polymorphism)构成了出色的动态特性..
虚函数:子类自己去实现。一旦一个函数被声明为虚函数,派生类自动成为虚函数,一级级传递。建议每一个都加virtual
抽象基类:
Abstract Class抽象类,无法实例化成一个对象。 将基类的虚函数定义为纯虚函数 virtual void f()=0;
抽象基类唯一目的就是让其派生类继承并实现它的接口方法。实现“接口与实现分离”。
纯虚函数告知编译器不用为函数编址,从而阻止该类的实例化。
抽象基类可分为好几层的抽象类。如第一层Shape 第二层Shape2D,Shape3D,抽象基类将提供丰富的接口函数供调用,这些都是public的虚函数,这样的类 叫做接口类。
抽象基类不能实例化,并且实现类被完全隐藏,所以必须以其他的途径获得实现类的对象,比如提供入口函数来动态创建实现类的对象,入口函数可以是全局函数,也可以使静态成员函数。
PubilcBase* create(){return new PublicBase; }
#include <iostream>
using namespace std;
class Base{
public:
virtual void f()=0;//纯虚函数,Base为抽象基类
virtual void g()=0;//纯虚函数
static Base* create();//抽象基类无法实例化,
//创建静态成员函数来动态创建类的对象。
//入口函数
};
class B:public Base
{
public:
virtual void f(){}//自动为虚函数,加上virtual更好
virtual void g(){}
};
Base* Base::create(){return new B;}//注意静态成员函数写在外面!
int main()
{
Base* b = Base::create();
b->f();
b->g();
return 0;
}
动态绑定:父类指针指向子类对象,可以调用虚函数。
每一个具有虚函数的多态类的对象内存有4个字节的大小存放虚表指针,指向虚函数表,虚函数表本质是一个函数指针数组,存放着这个类所有的虚函数的地址,包括那些继承的但未改写的虚函数。
#include <iostream>
using namespace std;
class A{
int a;
public:
virtual void f(){}
};
class B:public A{
public:
virtual void f(){}
virtual void f(int n){}
};
int main()
{
A* p = new B;
p->f();//正确
p->f(1);//编译错误,A类无此类原型函数,除非p类型为B*
//派生定义中的名字(对象或者函数名)将会义无反顾的遮蔽掉
//基类中任何的同名的对象或者函数。
return 0;
}
派生类定义了一个与其基类的虚函数同名的函数,但是参数表不同,编译器不会认为这个是对虚函数的改写,而是隐藏。所以不会发生运行时绑定,相反,要想达成运行时绑定的效果,同名虚函数必须相同的原型,即参数表相同(返回类型可以不同,C++特征===协变)
虚函数面临的难题:抽象基类无法实例化,基类指针指向派生类对象,并不能告诉我们到底是哪一个子类对象,因而不能进行相应的处理,仅有的静态类型检查和虚函数机制不足以解决所有的问题。所以我们有了RTTI(Run-time Type Identification)
RTTI:
typeid:typeid()运算符以一个对象或者类型名作为参数,返回一个匹配的const type_info对象,它表明该对象的确切类型。
常用的三个成员函数; operaotr==(),operator()!=(),name()
if(typeid(Base* p)==typeid(child)) ====>通过基类指针判断类型
dynamic_cast<>运算符:派生类对象应该也是基类的对象,typeid()不存在这种功能
dynamic_cast<>可以转换指针和引用,不能转换对象。
基类指针或引用===>子类指针或引用(downcast) 子类指针或引用====>基类指针或引用(upcast)
dynamic_cast<目标类型>(被转换的类型) 目标类型为某种类型的指针(包括void*),成功转换则返回目标类型 的指针,否则返回NULL
目标类型为某种类型的引用,成功则返回目标类型引用,失败返回std::bad_cast,不存在NULL引用。
注意:
1.dynamic_cast<>转换引用,需要catch std::bad_cast
2.试图用typeid来检索Null所指对象的类型,typeid(*p) 将抛出bad_typeid异常
3.dynamic_cast<>转换指针,记住返回值是否为NULL。