类和对象(多态)
美丽新科技BLOG

类和对象(多态)

刘纪彤
2年前发布

类和对象(多态)

多态

多条是C++面向对象的三大特性之一

多态是指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指针或引用调用成员函数,执行不同的函数。

多态的分类

多态分为两种:编译时多态和运行时多态。

  • 编译时多态:函数重载和运算符重载属于编译时多态,因为函数重载和运算符重载是静态的,函数地址早绑定。
  • 运行时多态:虚函数和抽象类属于运行时多态,因为虚函数和抽象类是动态的,函数地址晚绑定。

案例:

//多态-1
#include <iostream>
using namespace std;
class Base
{
public:
     void func()
     {
        cout<<"Base func"<<endl;
     }
};
class Derived : public Base
{
    void func()
    {
        cout << "Derived func()" << endl;
    }
};
int main()
{
    Base *p = new Derived();
    p->func();
    return 0;
}

运行结果:

Base func()

说明并未发生重写,而是发生了隐藏,因为在编译时,编译器会根据指针的类型来决定调用哪个函数,而不是根据指针所指向的对象的类型来决定调用哪个函数。

但是,如果将函数声明为虚函数,就可以实现运行时多态,如下所示:

//多态-2
#include <iostream>
using namespace std;
class Base
{
public:
    virtual void func()
    {
        cout << "Base func()" << endl;
    }
};
class Derived : public Base
{
    public:
    void func()
    {
        cout << "Derived func()" << endl;
    }
};
int main()
{
    Base *q= new Base();
    Base *p = new Derived();
    Derived *d = new Derived();
    q->func();
    p->func();
    d->func();
    return 0;
}
Base func()
Derived func()
Derived func()

显然的,这里的多态是运行时多态,因为在运行时,编译器会根据指针所指向的对象的类型来决定调用哪个函数。

总结:

多态满足条件

  • 有继承关系
  • 子类重写父类中的虚函数

多态使用条件

  • 父类指针或引用指向子类对象

相关概念:重写:函数返回值类型 函数名 参数列表 完全一致称为重写

多态的实现

多态的实现有两种方式:虚函数和抽象类。

  • 虚函数:虚函数是在基类中使用关键字virtual声明的函数,虚函数的特点是在运行时才动态绑定,可以实现运行时多态。
  • 抽象类:抽象类是包含纯虚函数的类,抽象类的特点是不能实例化对象,只能作为接口使用。

多态的好处

多态的好处是提高了代码的扩展性和复用性。

  • 提高了代码的扩展性:通过多态,可以在不修改原有代码的基础上,对原有代码进行扩展,增加新的功能。
  • 提高了代码的复用性:通过多态,可以在不修改原有代码的基础上,对原有代码进行复用,增加新的功能。

多态的弊端

多态的弊端是降低了代码的可读性和可维护性。

  • 降低了代码的可读性:通过多态,可以在不修改原有代码的基础上,对原有代码进行扩展,增加新的功能。
  • 降低了代码的可维护性:通过多态,可以在不修改原有代码的基础上,对原有代码进行复用,增加新的功能。

虚函数

虚函数是在基类中使用关键字virtual声明的函数,虚函数的特点是在运行时才动态绑定,可以实现运行时多态。

虚函数的声明

虚函数的声明格式如下:

virtual 返回值类型 函数名(参数列表);

纯虚函数和抽象类

纯虚函数是在基类中使用关键字virtual声明的函数,并在函数后面加上=0,纯虚函数的特点是没有函数体,只有函数原型,不能被调用,只能被重写。

抽象类是包含纯虚函数的类,抽象类的特点是不能实例化对象,只能作为接口使用。

纯虚函数和抽象类的声明格式如下:

class 类名
{
public:
    virtual 返回值类型 函数名(参数列表) = 0;
};

只要有纯虚函数的类,就是抽象类,抽象类不能实例化对象,只能作为接口使用。

使用时注意:

  • 抽象类无法实例化对象
  • 子类必须重写父类中的纯虚函数,否则也属于抽象类

示例:

//多态-3
#include <iostream>
using namespace std;
class Base
{
public:
    virtual void func() = 0;
};
class Derived : public Base
{
    public:
    void func()
    {
        cout << "Derived func()" << endl;
    }
};
class Derived2 : public Base
{
    public:
    void func()
    {
        cout << "Derived2 func()" << endl;
    }
};
int main()
{
    Base *p = new Derived();
    Derived2 d2;
    p->func();
    d2.func();
    return 0;
}
Derived func()
Derived2 func()

子类重写父类中的虚函数,就可以实现运行时多态。

虚析构函数和纯虚析构函数

虚析构函数是在基类中使用关键字virtual声明的析构函数,虚析构函数的特点是在运行时才动态绑定,可以实现运行时多态。

纯虚析构函数是在基类中使用关键字virtual声明的析构函数,并在函数后面加上=0,纯虚析构函数的特点是没有函数体,只有函数原型,不能被调用,只能被重写。

虚析构函数和纯虚析构函数的声明格式如下:

virtual ~类名();
virtual ~类名() = 0;

使用纯虚析构的好处是可以防止内存泄漏,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。所以需要虚析构函数或者纯虚析构函数。

使用纯虚析构函数的时候同样不能实例化对象,只能作为接口使用。

虚析构示例:

//多态-4
#include <iostream>
using namespace std;
class Base
{
public:
    virtual ~Base()
    {
        cout << "Base析构函数" << endl;
    }
};
class Derived : public Base
{
public:
    ~Derived()
    {
        cout << "Derived析构函数" << endl;
    }
};

void test01()
{
    Base *p = new Derived();
    delete p;
}
int main()
{
    test01();
    return 0;
}
Derived析构函数
Base析构函数

纯虚析构示例:

//多态-5
#include <iostream>
using namespace std;
class Base
{
public:
    virtual ~Base() = 0;
};
Base::~Base()
{
    cout << "Base析构函数" << endl;
}
class Derived : public Base
{
public:
    int *p;
    Derived()
    {
        p = new int(10);
    }
    ~Derived()
    {
        cout << "Derived析构函数" << endl;
        //释放堆区数据
        if (p != NULL)
        {
            delete p;
            p = NULL;
        }
    }
};

void test01()
{
    Base *p = new Derived();
    delete p;
}
int main()
{
    test01();
    return 0;
}
Derived析构函数
Base析构函数
喜欢就支持一下吧
点赞 0 分享 收藏
评论 抢沙发
OωO
取消