博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

C/CPP-多态

Posted on 2023-03-13 09:41  乔55  阅读(26)  评论(0编辑  收藏  举报

多态基本概念

// 什么是多态
- 多态:由继承产生的相关的不同的类,其对象对同一消息作出不同响应
- 多态性:允许同一函数或操作符有不同的版本,不同对象执行不同的版本时具有多态性
- 编译时的多态性:表现为函数名、操作符重载
- 运行时的多态性:通过派生类和虚继承类来实现
- 多态主要是为了实现程序的通用性


// 多态的特点
- 调用虚函数执行的是动态绑定,到底是绑定到哪个子类上,在运行时才知道到底绑定到哪
- 父类指针可以指向子类对象,是因为编译器帮我们做了隐式的做子类到父类的指针转换
- 单纯的继承关系时,可以这样:   parent* pp = new child;  // 只能父类指向子类 
- 有虚函数的继承关系时,不仅可以让父类指向子类,还可以让子类指针指向父类对象
- child* cp = dynamic_cast<child*>(pp);
- parent px; child* cp = dynamic_cast<child*>(&px);



// 虚函数特点
- 子类中定义的虚函数必须与父类中虚函数同名、同参数列表、同返回类型,否则认为是重载
- 例外:子类中虚函数返回子类指针,父类中返回父类指针,这也是多态
- 静态成员函数,是同一类对象所共有,不受限于某个对象,不能作为虚函数
- 只有类的成员函数才能被声明为vritaul
  - 继承自父类的virual函数,在子类体系中,virtual可加可不加,都认为是virtual
  - virtual只在类内声明、定义时加virtual,在类外定义是不能加virtaul
- 内联函数中每个对象有一个拷贝,无映射关系,不能作为虚函数
- 构造函数不能被定义为虚函数,因在调用构造函数时对象还未完成实例化。
- 析构可定义为虚函数,父、子类均动态分配空间时,必,须把析构声明为虚函数,实现撤销多态性
- 虚函数会增加内存开销,因为有虚函数表

// 多态的条件
- 要有继承
- 要有虚函数重写:在父类成员函数前加virtual
  - 在子类A中,重写一个show函数,做一操作
  - 在子类B中,重写一个show函数,做另一操作
  - 这些操作,就完成了对同一消息作出不同响应的功能
- 要有父类指针或引用指向子类对象





// 重载、重写(覆盖-多态实现的本质)与重定义(隐藏)
- 重载:一定是同一作用域内发生的
- 重写:父类中有某个虚函数A,子类中重写一个与该虚函数同名同参的函数,来覆盖该虚函数A
  - 重写时,函数名相同,参数相同,父类函数有vritaul声明
  - 重写时,作用域不同,一个函数在父类,一个函数在子类 
  - 父类的virtaul成员函数被子类重写,此函数会发生多态
  - 在子类中重写父类同名同参函数,也可以直接加voerride,覆盖父类该函数
  - 重写父类虚函数show(),怎样让父类指针能够访问子类函数show()
    - 重写后,将子类虚函数表中show函数的地址覆盖在父类虚函数表中show函数地址处
    - 当父类指针指向子类对象时,该指针虽然只能访问子类对象中的父类成员
    - 但当指针指向父类虚函数表show函数处地址时,该处填写的却是子类show函数的地址
    - 调用的自然就是子类的show函数
    - 可见,子类中重写一个父类虚函数,就可执行一个新的操作
    - 在不同子类中重写不同的虚函数,就可执行不同的操作,这就是多态的含义
    - 上边子类虚函数表中重写函数的地址填充在父类虚函数表中被重写函数处,就叫覆盖

- 重定义(隐藏):子类中重定义一个与父类同名的函数(父类函数不能是virtual,否则是重写)
- 隐藏的直观概念:某函数在子类中存在,但通过子类对象无法直接调用该函数。存在却不可用
- 父类:void show();void show(int x);在子类中:void show();
- 在子类中,重定义的show()就会隐藏掉继承自父类的所有的同名方法
- child c; c.show();  // 调用子类中重定义的show函数,无法调用继承自父类的show函数
- 若子类没有重定义show()函数,则:c.show();  c.show(x);  // 均是调用父类函数
- 


// 虚函数表
- 若C++类中有虚函数,编译器就会对该类产生一个虚函数表
- 虚函数表有很多项,每一项都是一个指针,每个指针指向的是该类里各个虚函数入口地址
- 虚函数表项里,第1项不是指向虚函数入口地址,而是这个类所关联的type_info对象
- const type_info& ref = typeid(*pp);  // pp是父类指针指向子类对象
- 上述语句ref实际就是虚函数指向的子类对象。也就是type_info对象

// 纯虚函数
class animal
{
    virtual void eat() = 0;
    virtual void sleep() = 0;
};
class person :public animal
{
  // eat实现
  // sleep实现
};
- 纯虚函数是指被标明为不具体实现的虚拟成员函数。父类中虚函数的具体实现依赖于不同子类
- 包含纯虚函数的类是抽象类,抽象类无法实例化,不能定义对象,但可定义指针。
- 抽象类可以为所有的类提供一个公共的接口,用相同的接口,实现不同的功能