多态与虚函数

多态的基本概念

实现了多态机制的程序,可以使用同一个名字完成不同的功能。

多态分为编译时多态和运行时多态。

多态

静态多态在编译期间就可以确定函数的调用地址,并产生代码。

函数调用与代码入口地址的绑定需要在运行时刻才能确定,称为动态联编或动态绑定。

在类之间满足赋值兼容的前提下,实现动态绑定必须满足以下两个条件

  • 必须声明虚函数
  • 通过基类类型的引用或者指针调用虚函数。

虚函数

所谓虚函数,就是在函数声明时前面加了virtual关键字的成员函数。
声明虚函数成员的格式

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

通过基类指针实现多态

程序6-1 通过基类指针实现多态示例

#include 
using namespace std;

class A
{
    public:
        virtual void Print()
        {
            cout<<"A::Print"<Print();        //多态,目前指向基类对象,调用a.Print(),输出A::Print
    pa = pb;            //派生类指针赋给基类指针,pa指向派生类对象b
    pa->Print();        //多态,目前指向派生类对象,调用b.Print(),输出B::Print
    pa = &d;            //基类指针pa指向派生类对象d
    pa->Print();        //多态,目前指向派生类对象,调用d.Print(),输出D::Print
    pa = &e;            //基类指针pa指向派生类对象e
    pa->Print();        //多态,目前指向派生类对象,调用e.Print(),输出E::Print
    return 0; 
}

A::Print
B::Print
D::Print
E::Print

程序6-2 用基类指针访问基类对象及派生类对象

#include 
#include 
using namespace std;
class A
{
    public:
        void put_name(string s)
        {
            name = s;
        }
        virtual void print_name() const
        {
            cout<<"A::"<put_name("多态示例_名字");       //赋给基类对象A_obj
    cout<<"A_p->print_name的输出内容:\t";
    A_p->print_name();          //使用指针输出,A_obj中的值
    cout<<"A_obj.print_name()的输出内容:\t";
    A_obj.print_name(); //使用对象输出
    
    A_p = &B_obj;       //基类指针指向派生类对象 
    A_p->put_name("另一个名字");     //多态,赋给派生类对象B_obj 
    cout<<"A_p->print_name()的输出内容:\t";
    A_p->print_name();      //多态,使用指针输出,B_obj中的值,继承的name的值 
    cout<<"B_obj.print_name()的输出内容:\t";
    B_obj.print_name();     //使用对象输出,B_obj中的值,继承的name值 
    B_obj.put_phone("电话号码999"); //派生类对象赋值 
    
    cout<<"((B *)A_p)->print_phone()的输出内容:\t";
    ((B*)A_p)->print_phone();   //强制转换基类指针,输出的是派生类对象中的值 
    cout<<"B_obj.print_phone()的输出内容:\t\t";
    B_obj.print_phone();    //输出的是派生类对象中的值 
    return 0; 
}

A_p->print_name的输出内容:     A::多态示例_名字
A_obj.print_name()的输出内容:  A::多态示例_名字
A_p->print_name()的输出内容:   B::另一个名字,另一个名字
B_obj.print_name()的输出内容:  B::另一个名字,另一个名字
((B *)A_p)->print_phone()的输出内容:   电话号码999
B_obj.print_phone()的输出内容:         电话号码999

通过基类引用实现多态

通过基类指针调用虚函数时可以实现多态,通过基类的引用调用虚函数的语句也是多态的。

程序6-3 基类引用实现多态

#include 
using namespace std;

class A
{
    public:
        virtual void Print()
        {
            cout<<"A::Print"<

*多态的实现原理

多态的关键在于通过基类指针或引用调用一个虚函数时,编译阶段不能确定到底调用的时基类还是派生类的函数,运行时才能确定。

程序6-4 多态机制下对象存储空间的大小

#include 
using namespace std;

class A
{
    public:
        int i;
        virtual void func(){}
        virtual void func2(){}
};

class B:public A
{
    int j;
    void func(){}
};

int main()
{
    cout<

多态实例

在面向对象的程序设计中,使用多态能够增强程序的可扩充性。

使用多态也能起到精简代码的作用。

程序6-5 使用多态出来图形示例

#include 
#include 
using namespace std;

class CShape        //基类:图形类 
{
    protected:
        double acreage;     //图形的面积,子类可以访问
    public:
        CShape()
        {
//          cout<<"基类构造函数"<a = a;
            this->b = b;
            this->c = c;
        };
        CTriangle()
        {
//          cout<<"三角形无参构造函数"<a<<","<b<<","<c<<",面积="<acreage<>n;
    for(i=0;i>c;
        switch(c)
        {
            case 'R':case 'r':      //矩形
                cin>>temp1>>temp2;
                pr = new CRectangle(temp1,temp2);
                pr->setAcreage(pr->CalAcr());
                pShapes[i] = pr;
                break;
            case 'C':case 'c':
                cin>>temp1;         //圆形
                pc = new CCircle(temp1);
                pc->setAcreage(pc->CalAcr());
                pShapes[i] = pc;
                break;
            case 'T':case 't':      //三角形
                cin>>temp1>>temp2>>temp3;
                pt = new CTriangle(temp1,temp2,temp3);
                pt = new CTriangle(temp1,temp2,temp3);
                pt->setAcreage(pt->CalAcr());
                pShapes[i]= pt;
                break; 
        }   
    }   
    if(n == 1) cout<<"共有"<PrintInfo();
        delete pShapes[i];      //释放空间 
    }
    return 0; 
} 

3
C 6
R 7.6 8.2
T 3 4 5
共有3种图形,分别是:
圆。    半径=6,面积=113.04
矩形。  宽度=7.6,高度=8.2,面积=62.32
三角形。三条边分别是:3,4,5,面积=6

多态的使用

类的成员函数直接按可以互相调用。

在普通成员函数(静态成员函数、构造函数和析构函数除外)中调用其他虚成员函数也是允许的,并且是多态的。
程序6-6 在成员函数中调用虚函数

#include 
using namespace std;
class CBase
{
    public:
        void func1()        //不是虚函数
        {
            cout<<"CBase::func1()"<

程序6-7 在构造函数与析构函数中调用虚函数

#include 
using namespace std;
class A
{
    public:
        virtual void hello()
        {
            cout<<"A::hello"<

实现多态时,必须满足的条件是:使用基类指针或引用来调用基类中声明的虚函数。

程序6-8 多态与非多态的对比

#include 
using namespace std;

class A
{
    public:
        void func1()
        {
            cout<<"A::func1"<func2();    //多态 
    pa->func1();    //不是多态 
    pb->func1();    //多态 
    return 0;
}

C::func2
A::func1
C::func1

虚析构函数

声明虚析构函数的格式

virtual ~类名();

虚析构函数没有返回值类型,没有参数。

如果一个类的析构函数是虚函数,则由它派生的所有子类的析构函数也是虚析构函数。

程序6-9 不使用虚析构函数的情况

#include 
using namespace std;
class ABase
{
    public:
        ABase()
        {
            cout<<"ABase 构造函数"<

改写,将基类析构函数修改为虚析构函数。

virtual ~ABase()
        {
            cout<<"ABase 析构函数"<

纯虚函数喝抽象类

纯虚函数

纯虚函数是声明在基类中的虚函数,没有具体的定义,而由各派生类根据识记需要给出各自的定义。

声明纯虚函数的格式

virtual 函数类型 函数名(参数表)  = 0;

抽象类

包含纯虚函数的类称为抽象类。

纯虚函数和函数体为空的虚函数的区别:

  • 纯虚函数没有函数体,而空的虚函数的函数体为空。
  • 纯虚函数所在的类是抽象类,不能直接进行实例化;而空的虚函数所在的类是可以实例化的。
    共同特点是:都可以派生出新的类,然后在新类中给出虚函数的实现,而且这种新的实现具有多态特征。

程序6-10 抽象类示例

#include 
using namespace std;

class A
{
    private:
        int a;
    public:
        virtual void print() = 0;
        void func1()
        {
            cout<<"A_func1"<print();
    B b;
    A *pc = &b;
    pc->func1();
    return 0;
}

B_print
A_func1

虚基类

定义虚基类的格式

class 派生类名 : virtual 派生方式 基类名
{
  派生类体
};

程序6-11 虚基类

#include 
using namespace std;
class A
{
    public:
        int a;
        void showa()
        {
            cout<<"a="<

你可能感兴趣的:(多态与虚函数)