C++ 虚继承和多态性

类体系的函数重载

  1. 在一个类中声明重载;
  2. 派生类定义和基类同名,同参数的函数,即在派生类中重载。

类指针的关系

  1. 基类指针指向基类对象;
  2. 派生类指针指向派生类对象;
  3. 基类指针指向派生类对象;
  4. 派生类指针指向基类对象;
  5. 基类指针可以直接指向派生类对象,但是使用的成员都是自己的版本,只有强制类型转换为派生类指针才能使用派生类特有成员;派生类指针不能直接指向基类对象,只有强制类型转换为基类指针才能指向

基类指针指向派生类对象

  1. 基类指针只能引用基类成员;
  2. 如果试图引用派生类中特有的成员,必须通过强制类型转换;
  3. 任何引用同名的成员,都是基类的版本
#include<iostream>
using namespace std;

class Base{
public:
    int ba;
    Base(int x){ba = x;cout<<"Base build!"<<endl;}
    void show(){cout<<"Base.show()"<<endl;}
};

class Detrived:public Base{
public:
    int da;
    int db;
    Detrived(int m,int n,int x):Base(x){da = m;db = n;cout<<"Detrived build!"<<endl;}
    void show(){cout<<"Detrived.show()"<<endl;}
    void print(){cout<<"Detrived.print()"<<endl;}
};

int main(){
    Base* pb = new Detrived(1,2,3);
    pb->show();
    cout<<pb->ba<<endl;
    // cout<<pb->da<<endl;   // 错误
    cout<<((Detrived*)pb)->da<<endl;   // 正确,强制类型转换
}
结果:
Base build!
Detrived build!
Base.show()
3
1

派生类指针指向基类对象

  1. 只有通过强制类型转换之后,才能引用基类对象
Detrived* p = new Base();   // 错误
Base b;
Detrived* p = &b;  // 错误
(Base*)p = &b;  // 正确

虚函数

背景

基类指针可以指向派生类对象,但是通过基类指针访问的只能是基类的成员。C++ 中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。使基类指针依赖运行时的地址值调用不同版本的成员函数

定义

基类中想要说明为虚函数的成员函数前面加上

virtual

关键字,就将该成员函数说明为虚函数,之后,派生类中同名的函数也默认具有虚特性而可以省略此关键字。

  1. 一个函数被说明为虚函数,不论经历多少派生层次,所有同界面(同名同参数同返回类型)的重载函数都保持虚特性,因为派生类也是基类;
  2. 虚函数必须是类的成员函数,不能将虚函数声明为全局函数或静态成员函数,因为虚函数的动态联编依靠 this 指针实现;
  3. 不能将友元说明为虚函数,但是虚函数可以是另一个类的友元;
  4. 析构函数可以是虚函数,但是构造函数不能是。
  5. 虚函数是函数重载的一种特殊形式,不同于一般的函数重载。一般的函数重载仅仅要求函数名相同,重载虚函数要求函数界面完全一致(同名同参数同返回类型)
  6. 虚函数在基类中说明,派生类中自动定义。

实例

#include<iostream>
using namespace std;

class Base{
public:
    virtual void show(){cout<<"Base"<<endl;}
};

class DetrivedA:public Base{
public:
    void show(){cout<<"DetrivedA"<<endl;}  // 不用声明 virtual,默认具有
};

class DetrivedB:public DetrivedA{
public:
    void show(){cout<<"DetrivedB"<<endl;} // 不用声明 virtual,默认具有
};

int main(){
    Base* p = new Base();
    p->show();
    p = new DetrivedA();
    p->show();
    p = new DetrivedB();
    p->show();
}
结果:
Base
DetrivedA
DetrivedB

虚函数的重载特性

类层次的各个虚函数,表面上它们界面相同,但是隐含的 this 指针是不同的,其关联类型分别是重载它们的派生类。虚函数是仅由 this 指针类型区分接口的函数C++ 的“虚”特性仅负责在程序运行时把基类 this 指针的关联类型转换成当前指向对象的类类型,而不能改变函数其他参数的性质。

虚析构函数

在继承下,用基类指针指向派生类对象时,然后 delete 基类指针,只会调用基类的析构函数,派生类的析构函数不调用,其资源难以释放,此时可以将派生类声明为虚函数,将基类和派生类的资源一起释放。

#include<iostream>
using namespace std;

class Base{
public:
    ~Base(){cout<<"delete Base"<<endl;}
};

class DetrivedA:public Base{
public:
    ~DetrivedA(){cout<<"delete DetrivedA"<<endl;}
};

int main(){
    Base* p = new DetrivedA();
    delete p;
}
结果:
delete Base

将析构函数声明为虚函数之后:

#include<iostream>
using namespace std;

class Base{
public:
    virtual ~Base(){cout<<"delete Base"<<endl;}
};

class DetrivedA:public Base{
public:
    ~DetrivedA(){cout<<"delete DetrivedA"<<endl;}
};

int main(){
    Base* p = new DetrivedA();
    delete p;
}
结果:
delete DetrivedA
delete Base

纯虚函数

定义是:

virtual 类型 函数名(参数表) = 0;
  1. 纯虚函数在基类中定义;
  2. 比较虚函数后面多了一个 = 0;
  3. 纯虚函数只需要声明,不需要定义也不能定义;
  4. 所有的派生类都必须实现这个纯虚函数。

抽象类

  1. 一个具有纯虚函数的基类叫作抽象类;
  2. 抽象类至少要有一个纯虚函数;
  3. 如果抽象类的一个派生类没有为继承的纯虚函数实现自己的版本,那么它依旧是抽象类
  4. 抽象类只能作基类;
  5. 抽象类不能建立对象;
  6. 抽象类不能用作参数类型、函数返回类型或显式转换类型;
  7. 抽象类可以说明指针和引用,它的指针不受上面一条的限制。
#include<iostream>
using namespace std;

class Animals{
    string name;
public:
    Animals(string n){name = n;}
    virtual void makeSound() = 0;
};

class Dog:public Animals{
public:
    Dog(string n):Animals(n) {}
    void makeSound(){
        cout<<"Dog:WangWang!"<<endl;
    }
};

class Cat:public Animals{
public:
    Cat(string n):Animals(n) {}
    void makeSound(){
        cout<<"Cat:MiaoMiao!"<<endl;
    }
};

int main(){
    // Animals animals[3];   错误,抽象类不能创建数组
    Animals* animal[3] = {new Dog("A"),new Cat("b"),new Cat("c")};
    for(int i=0;i<3;i++)
        animal[i]->makeSound();
}
结果:
Dog:WangWang!
Cat:MiaoMiao!
Cat:MiaoMiao!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值