4、多态

类型兼容原则
在基类对象的任何地方,都可以使用公有派生类的对象来代替

1子类对象可以当作父类对象使用 【第二层】
2子类对象可以直接赋值给父类  【第二层】
3子类对象可以直接初始化父类 【第二层】

4父类指针可以指向子类对象 【第一层】
5父类引用可以直接指向子类对象【第一层】

【第一层含义】父类指针(引用)指向子类对象

【第二层含义】子类对象初始化父类对象

一个接口,多种方法
多态成立的三个条件
继承
虚函数重新
父类指针(引用)指向子类

多态的理论基础
静态联编:程序在编译的时候,就已经知道怎么去执行
动态联编:程序在执行的过程中,才决定去调用那种类型
virtual c++编译器会对virtual 关键字特殊处理:
在运行的时候根据具体的对象,执行不同的函数,变现成多态

虚析构函数
通过父类指针 释放 所有的子类资源


#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;


//虚析构函数
class A
{
public:
    A()
    {
        p = new char[20];
        strcpy(p, "obja");
        printf("A()\n");
    }
     virtual ~A()
    {
        delete [] p;
        printf("~A()\n");
    }
protected:
private:
    char *p;
};

class B : public A
{
public:
    B()
    {
        p = new char[20];
        strcpy(p, "objb");
        printf("B()\n");
    }
      ~B()
    {
        delete [] p;
        printf("~B()\n");
    }
protected:
private:
    char *p;
};


class C : public B
{
public:
    C()
    {
        p = new char[20];
        strcpy(p, "objc");
        printf("C()\n");
    }
    ~C()
    {
        delete [] p;
        printf("~C()\n");
    }
protected:
private:
    char *p;
};



//只执行了 父类的析构函数
//向通过父类指针  把 所有的子类对象的析构函数 都执行一遍
//向通过父类指针 释放所有的子类资源 
void howtodelete(A *base)
{
    delete base;  //这句话不会表现成多态 这种属性
}

/*
void howtodelete(B *base)
{
    delete base;  //这句话不会表现成多态 这种属性
}
*/
void main()
{
    C *myC = new C; //new delete匹配
    //
    delete myC; //直接通过子类对象释放资源 不需要写virtual 

    //howtodelete(myC);

    cout<<"hello..."<<endl;
    system("pause");
    return ;
}

接口(抽象类)


#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

//多态的三个条件:
//1.继承
//2.重写父类虚函数
//3.父类引用指向子类

#define MAX 100
//抽象类
class Drink//-----------------------------------接口(抽象类)
{
public:
    //煮水
    virtual void Boil() = 0;
    //冲泡
    virtual void Brew() = 0;
    //导入杯中
    virtual void PourInCup() = 0;
    //加辅料
    virtual void AddSomething() = 0;

    //模板方式确定函数调用顺序
    void Mk()
    {
        Boil();
        Brew();
        PourInCup();
        AddSomething();
    }
};

class Coffee :public Drink//--------------------------------实现接口(子类)
{

public:
    //煮水
    virtual void Boil()
    {
        cout << "主电路谁" << endl;
    }
    //冲泡
    virtual void Brew()
    {
        cout << "从跑" << endl;
    }
    //导入杯中
    virtual void PourInCup()
    {
        cout << "拿铁" << endl;
    }
    //加辅料
    virtual void AddSomething()
    {
        cout << "加加啊加啊肯定是积分" << endl;
    }
};

class Tea :public Drink//--------------------------------实现接口
{

public:

    //煮水
    virtual void Boil()
    {
        cout << "烧开水" << endl;
    }
    //冲泡
    virtual void Brew()
    {
        cout << "从跑" << endl;
    }
    //导入杯中
    virtual void PourInCup()
    {
        cout << "铁观音-----" << endl;
    }
    //加辅料
    virtual void AddSomething()
    {
        cout << "加水水水水水水水水水水积分" << endl;
    }
};
void Maker(Drink *dk)//---------------------------------------业务(抽象类里面的纯虚函数)
{
    dk->Mk();
}

void test01()
{
    Drink *dk = NULL;
    dk = new Coffee;
    Maker(dk);
    delete dk;
    cout << "------------------------------------" << endl;
    dk = new Tea;
    Maker(dk);
    delete dk;
}

int main()
{
    test01();
    system("pause");
    return 0;
}

多态原理

这里写图片描述


C++中多态实现的原理:
1.当类中声明虚函数时,编译器会在类中生成一个虚函数表
2.虚函数表是一个存储类成员函数指针的数据结构
3.虚函数表是由编译器自动生成与维护的
4.Virtual成员函数会被编译器放入虚函数表中
5.存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr指针)

多态的实现原理:(体现c++编译器动手脚的三个地方)
1.当类里面有virtual函数时,c++编译器会每个类添加一个虚函数表,把这函数添加到虚函数表里面
2.在定义对象时,c++编译器会给这个对象添加一个vptr指针,这个指针指向虚函数表(提前布局操作)
3.在调用时,c++编译器,会通过vptr指针去找表里面的函数,实现动态联编,就发生多态

面试 对多态的理解:
1.现象:1.同样的调用语句有不同的变现形式,同一个函数在父类子类穿梭的时候表现不同形态
2.实现的方法:有继承,有virtual函数重写,有父类指针指向子类对象
3.多态原理:动态联编
4.多态的实现,virtual关键字,告诉编译器这个函数要支持多态,不要根据指针类型判断如何调用,而是要根据指针所指向的实际对象类型来进行判断如何调用
5.多态的重要意义:设计模式的基础,是框架的基础
6.多态实现的理论基础;函数指针左函数参数

证明vptr指针的存在
通过打印对象的大小也可证明


class Father 
{
    int id;

public:
    int age;
    virtual void func() 
    {
        cout <<"123412341234调用虚函数" << endl;
    }
};

void test01() 
{
    Father fa;

    //保存vptr指针
    int *vptr = NULL;
    //vptr是函数的第一成员变量
    memcpy(&vptr,&fa,4);
    printf("%p\n",vptr);
    //定义函数指针
    typedef void (*vptrFunc)();
    vptrFunc p1 = (vptrFunc) *vptr;
    //cout << *((int *)*(int *)&fa) << endl;
    p1();
}

vptr指针分步初始化

子类Vptr指针的初始化过程
构造函数从父类的构造开始调用
1.当调用父类的构造函数时,子类的vptr指针被初始化,指向父类的虚函数表
2.当执行完毕父类的构造函数时,子类的vptr指针被重新赋值,指向子类的虚函数表

动手脚的地方
Virtual  void func();//做特殊出处理,添加一个vptr指针,把虚函添加到虚函数表中
pBase->func();//传来父类调用父类,传来子类调用子类
//判断条件,增加判断条件,在不知道的情况下增加判断条件

纯虚函数和抽象类
含有纯虚函数的类–抽象类
面向抽象类编程
抽象类多继承标准语法


#include <iostream>
using namespace std;

class Interface1
{
public:
    virtual int add(int a, int b) = 0;
    virtual void print() = 0;
};

class Interface2
{
public:
    virtual int mult(int a, int b) = 0;
    virtual void print() = 0;
};

class Parent
{
public:
    int getA()
    {
        a = 0;
        return a;
    }
protected:
private:
    int a;
};

class  Child : public Parent, public Interface1, public Interface2
{
public:
    virtual int add(int a, int b)
    {
        cout<<"Child: add()已经执行\n";
        return a + b;
    }

    virtual void print()
    {
        cout<<"Child: print()已经执行\n";
    }

    virtual int mult(int a, int b)
    {
        cout<<"Child: mult()已经执行\n";
        return a*b;
    }
protected:
private:
};

void main71()
{

    Child c1;
    c1.print();

    Interface1 *it1 = &c1;
    it1->add(1, 2);

    Interface2 *it2 = &c1;
    it2->mult(3, 6);


    cout<<"hello..."<<endl;
    system("pause");
    return ;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值