第十二章 C++ 面向对象设计方法概述

继承: 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。

                                                  

                               

发布了37 篇原创文章 · 获赞 2 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章