c++—多态、虚函数

1. 多态定义:不同的派生类对于基类的同一接口的不同反应;

    作用:提高程序的扩展性,减轻系统升级、维护、调试的工作量和复杂度;

2. 向上类型转型:派生类→基类; 向下类型转型:基类→派生类;

3. 多态产生的条件

    (1)存在继承关系;(2)派生类重写基类的方法,基类的方法用virtual修饰;(3)用基类的指针或引用调用派生类对象的指针或引用;

4. 多态的分类

    (1)静多态(编译时绑定):①函数重载,通过函数重命名在编译阶段决定;②模板;

    (2)动多态(运行时绑定):(继承中的)虚函数;

5. 虚函数注意事项

    (1)基类中利用virtual修饰析构函数,可以在利用基类指针释放派生类指针时使用;即基类的析构函数也是虚函数;

    (2)可以利用关键字override放在派生类虚函数后面,用来派生类的这个函数有没有遮蔽/覆盖基类中的虚函数;

    (3)在派生类中覆写基类中的函数时,要求函数名、返回值类型、函数参数个数及类型必须全部匹配;

    (4)关键字final,若放在类的后面,如class Test final{},表示禁止其他类继承;若final修饰虚函数时,则禁止子类重载;

    (5)一些限制

        ①只有类的成员函数才能声明为虚函数;

        ②静态成员函数(被static修饰的)不能是虚函数;

        ③内联函数不能是虚函数;

        ④构造函数不能是虚函数(因为构造函数本身就是给自己的类初始化的);

        ⑤析构函数可以是虚函数且通常声明为虚函数(详见5-(1));

多态示例:

#include <iostream>

using namespace std;

class A
{
public:
    A()
    {
    }

    virtual void print()
    {
        cout<<"print A"<<endl;
    }

    virtual ~A()
    {
        cout<<"~A"<<endl;
    }
};

class B : public A
{
public:
    B()
    {
    }

    void print()
    {
        cout<<"print B"<<endl;
    }

    ~B()
    {
        cout<<"~B"<<endl;
    }
};

class C : public A
{
public:
    C()
    {
    }

    void print()
    {
        cout<<"print C"<<endl;
    }
  
};

void test(A *a)
{
    a->print();
}

int main(int argc, char **argv)
{
    A a;
    B b;
    C c;
    test(&a);  //这里分别打印ABC类自己的成员函数,实现多态
    test(&b);
    test(&c);

    A *a1;
    B *b1 = new B();
    a1 = b1;
    delete a1;  //这里利用基类的指针释放派生类的指针,就需要将基类的析构函数变更为虚函数,前面加virtual,否则只析构A

    return 0;
}

6. 抽象类

    (1)纯虚函数定义:是指在一个类里的成员函数,即是虚函数(virtual修饰的),同时也只有声明没有定义,则是虚函数,形式如下:

virtual test(int num, int len) = 0;

    (2)抽象类定义:如果一个类里面有纯虚函数,那么这个类就是抽象类;

    (3)抽象类的特点:只可以定义指针或引用,不可以实例化对象;

    (4)抽象类的作用:充当功能接口或者类接口,依赖于抽闲类编程;

    (5)派生类继承的时候,必须全部将基类的纯虚函数覆写并定义,若遗漏一个,那么该派生类也是抽象类,也无法实例化对象;

抽象类继承示例:

#include <iostream>

using namespace std;

class A
{
public:
    A()
    {
    }

    virtual void print(int num, int len) = 0;

};

class B : public A
{
public:
    B()
    {
    }

    void print(int num, int len)
    {
        
        cout<<"print B = "<<(num+len)<<endl;  //+
    }

};

class C : public A
{
public:
    C()
    {
    }

    void print(int num, int len)
    {
        cout<<"print C = "<<(num*len)<<endl;  //*
    }
};  

int main(int argc, char **argv)
{
    A *a1 = new B();
    A *a2 = new C();

    a1->print(3,4);
    a2->print(3,4);
    
    return 0;
}

    (6)依赖倒置原则:要依赖于抽象,让变化的依赖于不变的,妈妈讲故事的案例,妈妈要讲很多故事,要制作一个故事的抽象类,这样有新的故事时,扩展性就很好,即不用修改现有代码,只需要新增类去继承这个故事的抽象类即可,如下所示:

#include <iostream>
#include <string>

using namespace std;

class AbsStory
{
public:
    virtual string getcontant() = 0;
};

class Book1:public AbsStory
{
public:
    string getcontant()
    {
        return "西游记";
    }
};

class Book2:public AbsStory
{
public:
    string getcontant()
    {
        return "哪吒脑海";
    }
};

class Book3:public AbsStory
{
    string getcontant()
    {
        return "拔苗助长";
    }
};

class Mother
{
public:
    void tell_story(AbsStory *b)
    {
        cout<<"妈妈讲";
        cout<<b->getcontant();
        cout<<"的故事"<<endl;
    }
};

int main(int argc, char **argv)
{
    AbsStory * s1;
    s1 = new Book3();  //讲不同的故事,只需要改123数字即可,新增故事,只需要新增类即可

    Mother m1;
    m1.tell_story(s1);




    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_72814368/article/details/130931161