C++【10】(多态)

127 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

一、动态多态

1.1 相关概念

多态:主要指函数的地址确定的时机,是在编译时确定还是在运行时确定

分类:

  1. 静态多态:函数的==地址在编译时确定==
    1. 函数重载
    2. 运算符重载
  2. 动态多态:函数的==地址在运行时确定==
    1. 虚函数
    2. 纯虚函数
    3. 虚析构
    4. 纯虚析构

1.2 虚函数

每一个函数定义完之后,代码中如果要调用这个函数,则在编译阶段就已经确定了函数的地址,所以当运行的时候直接调用即可;

这样做在父子类中如果有==同名的成员函数==,使用父类的指针保存子类地址之后,默认只能调用父类的成员函数,因为在编译阶段地址已经确定了

为了解决这个问题,可以==将同名的成员函数使用virtual修饰称之为虚函数==,如果将成员函数设置为虚函数,意味着编译阶段不确定要调用的函数的地址,而是在运行的时候根据保存的地址来确定调用的函数。

class MyCal
virtual int mymath(int m, int n)//不知道子类的情况,所以定义了一个没有操作的函数

class MyAdd:public MyCal
int mymath(int m, int n)
#include <iostream>

using namespace std;

class MyCal{
public:

    virtual int mymath(int m, int n)
    {
        cout << "这是一个计算器" << endl;

        return 0;
    }

    int m;
    int n;
};

class MyAdd:public MyCal{
public:
    int mymath(int m, int n)
    {
        cout << m << "+" << n << "=" << m+n << endl;

        return m + n;
    }
};

void test1()//通过子类实例化的对象来调用
{
    MyAdd m1;
    m1.mymath(100, 20);
}

void test2()//定义一个父类的指针,根据所需要的地址来调用所需要的函数
{
    MyCal *m1 = new MyAdd;
    m1->mymath(100, 20);
}

//全局函数,不知道有那些子类,所以传父类的地址,以及要操作的参数
//因为父类的指针保存子类的地址(上行转换)
int GetMath(MyCal *p, int m, int n)
{
    int num = p->mymath(m, n);

    return num;
}

void test3()
{
    MyAdd m1;
    int n = GetMath(&m1, 100, 20);
    cout << "n = " << n << endl;
}

int main()
{
    
    test3();
    return 0;
}

1.3 纯虚函数

  • 含有纯虚函数的类称之为==抽象类==
  • 抽象类不能实例化对象
  • 抽象类只能作为其他类的父类
virtual int mymath(int m, int n) = 0;//没有结构体

MyCal m;//这样不行
#include <iostream>

using namespace std;

class MyCal{
    
public:
    virtual int mymath(int m, int n) = 0;

    int m;
    int n;
};

class MyAdd:public MyCal{
public:
    int mymath(int m, int n)
    {
        cout << m << "+" << n << "=" << m+n << endl;

        return m + n;
    }
};

class MySub:public MyCal{
public:
    int mymath(int m, int n)
    {
        cout << m << "-" << n << "=" << m-n << endl;

        return m - n;
    }
};

class MyMul:public MyCal{
public:
    int mymath(int m, int n)
    {
        cout << m << "*" << n << "=" << m*n << endl;

        return m * n;
    }
};

class MyDiv:public MyCal{
public:
    int mymath(int m, int n)
    {
        cout << m << "/" << n << "=" << m/n << endl;

        return m / n;
    }
};

void test1()
{
    MyAdd m1;
    m1.mymath(100, 20);

    MySub m2;
    m2.mymath(100, 20);

    MyMul m3;
    m3.mymath(100, 20);

    MyDiv m4;
    m4.mymath(100, 20);
}

void test2()
{
    MyCal *m1 = new MyAdd;
    m1->mymath(100, 20);

    MyCal *m2 = new MySub;
    m2->mymath(100, 20);

    MyCal *m3 = new MyMul;
    m3->mymath(100, 20);

    MyCal *m4 = new MyDiv;
    m4->mymath(100, 20);
}

int GetMath(MyCal *p, int m, int n)
{
    int num = p->mymath(m, n);

    return num;
}

void test3()
{
    MyAdd m1;
    int n = GetMath(&m1, 100, 20);
    cout << "n = " << n << endl;
}

int main()
{
    test3();
    return 0;
}

1.4 虚析构

将父类的析构函数设置为虚析构,子类的同样也是默认如果父类的指针保存子类的地址,只会调用父类的析构函数,不会调用子类的析构函数,

Parent *p1 = new Son(1001, "张三", "法律边缘");
 p1->printMsg();
 delete p1;

如果子类中有增加的指针变量的成员,则可能需要动态开辟空间,需要调用析构函数释放空间,所以需要调用子类的析构函数,所以需要将父类的析构设置为虚析构函数。

如果将析构函数设置为虚析构,就可以调用子类的析构函数

virtual ~Parent()
#include <iostream>
#include <string.h>

using namespace std;

class Parent{
public:
    Parent()
    {
        cout << "Parent: 无参构造函数" << endl;
        this->id = 0;
        this->name = NULL;
    }

    Parent(int id, char *name)
    {
        cout << "Parent: 有参构造函数" << endl;
        this->id = id;
        this->name = new char[strlen(name) + 1];
        strcpy(this->name, name);
    }
    
//虚析构
    virtual ~Parent()
    {
        cout << "Parent: 析构函数" << endl;

        if(this->name != NULL)
        {
            delete []this->name;
            this->name = NULL;
        }
    }

    virtual void printMsg()
    {
        cout << this->id << ", " << this->name << endl;
    }

    int id;
    char *name;
};

class Son:public Parent{
public:
    Son()
    {
        cout << "Son: 无参构造函数" << endl;
        this->address = NULL;
    }

    Son(int id, char *name, char *address):Parent(id, name)
    {
        cout << "Son: 有参构造函数" << endl;
        this->address = new char[strlen(address) + 1];
        strcpy(this->address, address);
    }

    ~Son()
    {
        cout << "Son: 析构函数" << endl;
        if(this->address != NULL)
        {
            delete []this->address;
            this->address = NULL;
        }
    }

    void printMsg()
    {
        cout << this->id << ", " << this->name << ", " << this->address << endl;
    }

private:
    char *address;
};

void test1()
{
     Parent *p1 = new Son(1001, "张三", "北京明园大学");
     p1->printMsg();
     delete p1;
}

int main()
{
    test1();

    return 0;
}

1.5 纯虚析构

  • 纯虚析构函数需要在==类内声明==,==类外实现==
  • 实现的原因就是因为需要释放自己的空间
  • ==抽象类中一般都有纯虚析构函数==
Parent::~Parent()
#include <iostream>
#include <string.h>

using namespace std;

class Parent{
public:
    Parent()
    {
        cout << "Parent: 无参构造函数" << endl;
        this->id = 0;
        this->name = NULL;
    }

    Parent(int id, char *name)
    {
        cout << "Parent: 有参构造函数" << endl;
        this->id = id;
        this->name = new char[strlen(name) + 1];
        strcpy(this->name, name);
    }

//纯虚析构
    virtual ~Parent() = 0;

    virtual void printMsg()
    {
        cout << this->id << ", " << this->name << endl;
    }

    int id;
    char *name;
};

Parent::~Parent()
{
    cout << "Parent: 析构函数" << endl;

    if(this->name != NULL)
    {
        delete []this->name;
        this->name = NULL;
    }
}

class Son:public Parent{
public:
    Son()
    {
        cout << "Son: 无参构造函数" << endl;
        this->address = NULL;
    }

    Son(int id, char *name, char *address):Parent(id, name)
    {
        cout << "Son: 有参构造函数" << endl;
        this->address = new char[strlen(address) + 1];
        strcpy(this->address, address);
    }

    ~Son()
    {
        cout << "Son: 析构函数" << endl;
        if(this->address != NULL)
        {
            delete []this->address;
            this->address = NULL;
        }
    }

    void printMsg()
    {
        cout << this->id << ", " << this->name << ", " << this->address << endl;
    }

private:
    char *address;
};

void test1()
{
    Parent *p1 = new Son(1001, "张三", "北京明园大学");
    p1->printMsg();
    delete p1;
}

int main()
{
    test1();

    return 0;
}