mooc程序设计与算法(三)C++面向对象程序设计 多态

虚函数

构造函数和静态成员函数不能是虚函数

多态的表现形式:

派生类的指针可以赋值给基类指针

通过基类指针用基类和派生类中的同名虚函数时:

  1. 若该指针指向一个基类的对象,那么被调用是基类的虚函数
  2. 若该指针指向一个派生类的对象,那么被调用的是派生类的虚函数。

派生类的对象可以赋值给基类的引用

通过基类引用调用基类和派生类中的同名虚函数时:

  1. 若该引用引用的是一个基类的对象,那么被调用的是基类的虚函数
  2. 若该引用引用的是一个派生类的对象,那么被调用的是派生类的虚函数
    例子。。。。
#include <iostream>
using namespace std;
class A{
public:
    virtual void print(){
        cout<<"A::Print"<<endl;
    }
};
class B:public A{
public:
    virtual void print(){
        cout<<"B::Print"<<endl;
    }
};
class D:public A{
public:
    virtual void print(){
        cout<<"D::print"<<endl;
    }
};
class E:public B{
public:
    virtual void print(){
        cout<<"E::print"<<endl;
    }
};
int main()
{
    A a;B b;D d;E e;
    A *pa = &a;
    B *pb = &b;
    D *pd = &d;
    E *pe = &e;
    pa->print();
    pa = pb;
    pa->print();
    pa = pd;
    pa->print();
    pa = pe;
    pa->print();
    return 0;
}
A::Print
B::Print
D::print
E::print

多态的作用:可以增强程序的可扩充性,即:程序需要修改或增加功能的时候,需要改动和增加的代码较少

#include <iostream>
#include <stdlib.h>
#include <math.h>
using namespace std;
class CShape{
public:
    virtual double Area()=0;
    virtual void PrintInfo()=0;

};
class CRectangle:public CShape{
public:
    int w, h;
    virtual double Area();
    virtual void PrintInfo();
};
class CCircle:public CShape{
public:
    int r;
    virtual double Area();
    virtual void PrintInfo();
};
class CTrinale:public CShape{
public:
    int a,b,c;
    virtual double Area();
    virtual void PrintInfo();
};
double CRectangle::Area() {
    return w*h;
}
void CRectangle::PrintInfo() {
    cout<<"R:"<<Area()<<endl;
}
double CCircle::Area() {
    return 3.14*r*r;
}
void CCircle::PrintInfo() {
    cout<<"C:"<<Area()<<endl;
}
double CTrinale::Area() {
    int p = (a+b+c)/2;
    return sqrt(p*(p-a)*(p-b)*(p-c));
}
void CTrinale::PrintInfo() {
    cout<<"T:"<<Area()<<endl;
}
CShape *pShape[100];
int MyCompare(const void *s1, const void *s2){
    double a1, a2;
    CShape **p1;
    CShape **p2;
    p1 = (CShape**)s1;
    p2 = (CShape**)s2;
    a1 = (*p1)->Area();
    a2 = (*p2)->Area();
    if(a1<a2) return -1;
    else if(a2<a1) return 1;
    else return 0;
}
int main()
{
    int i;int n;
    CRectangle *pr;
    CTrinale *pt;
    CCircle *pc;
    cin>>n;
    for (i = 0; i < n; ++i) {
        char c;
        cin>>c;
        switch (c){
            case 'R':
                pr = new CRectangle();
                cin>>pr->h>>pr->w;
                pShape[i]=pr;
                break;
            case 'T':
                pt = new CTrinale();
                cin>>pt->a>>pt->b>>pt->c;
                pShape[i]=pt;
                break;
            case 'C':
                pc = new CCircle();
                cin>>pc->r;
                pShape[i]=pc;
                break;
        }
    }
    qsort(pShape, n, sizeof(CShape*), MyCompare);
    for (int i = 0; i < n; ++i) {
        pShape[i]->PrintInfo();
    }
}
/*
3
T 3 4 5
R 1 2
C 1
 */

下面用sort在补充一次。。。。在此感谢实验室的同门小妹妹,qsort中传入的是一个const void *类型,而sort传入的直接是一个对应的类型

#include <iostream>
#include <stdlib.h>
#include <math.h>
#include <algorithm>
using namespace std;
class CShape{
public:
    virtual double Area()=0;
    virtual void PrintInfo()=0;

};
class CRectangle:public CShape{
public:
    int w, h;
    virtual double Area();
    virtual void PrintInfo();
};
class CCircle:public CShape{
public:
    int r;
    virtual double Area();
    virtual void PrintInfo();
};
class CTrinale:public CShape{
public:
    int a,b,c;
    virtual double Area();
    virtual void PrintInfo();
};
double CRectangle::Area() {
    return w*h;
}
void CRectangle::PrintInfo() {
    cout<<"R:"<<Area()<<endl;
}
double CCircle::Area() {
    return 3.14*r*r;
}
void CCircle::PrintInfo() {
    cout<<"C:"<<Area()<<endl;
}
double CTrinale::Area() {
    int p = (a+b+c)/2;
    return sqrt(p*(p-a)*(p-b)*(p-c));
}
void CTrinale::PrintInfo() {
    cout<<"T:"<<Area()<<endl;
}
CShape *pShape[100];
bool MyCompare( CShape *s1,  CShape *s2) {
    double a1, a2;
    a1 = s1->Area();
    a2 = s2->Area();
    return a1>a2? true: false;
}
int main()
{
    int i;int n;
    CRectangle *pr;
    CTrinale *pt;
    CCircle *pc;
    cin>>n;
    for (i = 0; i < n; ++i) {
        char c;
        cin>>c;
        switch (c){
            case 'R':
                pr = new CRectangle();
                cin>>pr->h>>pr->w;
                pShape[i]=pr;
                break;
            case 'T':
                pt = new CTrinale();
                cin>>pt->a>>pt->b>>pt->c;
                pShape[i]=pt;
                break;
            case 'C':
                pc = new CCircle();
                cin>>pc->r;
                pShape[i]=pc;
                break;
        }
    }
//    qsort(pShape, n, sizeof(CShape*), MyCompare);
    sort(pShape, pShape+n, MyCompare);
    for (int i = 0; i < n; ++i) {
        pShape[i]->PrintInfo();
    }
}


在非构造函数,非析够函数的成员函数中调用虚函数,是多态,在构造函数和析够函数中调用虚函数,不是多态

#include <iostream>
using namespace std;
class myclass{
public:
    virtual void hello(){cout<<"hello from myclass"<<endl;}
    virtual void bye(){cout<<"bye from myclass"<<endl;}
};
class son:public myclass{
public:
    void hello(){cout<<"hello form son"<<endl;}
    son(){hello();}  //这里不是多态
    ~son(){bye();}
    // 派生类和基类中虚函数同名同参数表的函数,不加virtual也自动成为虚函数
};
class grandson:public son{
public:
    void hello(){cout<<"hello from grandson"<<endl;}
    void bye(){cout<<"bye from grandson"<<endl;}
    grandson(){cout<<"constructing grandson"<<endl;}
    ~grandson(){cout<<"destructing grandson"<<endl;}
};
int main(){
    grandson gson;
    son *pson;
    pson = &gson;
    pson->hello();
    return 0;
}
hello form son
constructing grandson
hello from grandson
destructing grandson
bye from myclass

在构造函数和析够函数中调用虚函数,不是多态。编译时即可确定,调用的函数是自己的类或基类中定义的函数,不会等到运行时才决定调用自己的还是派生的函数。

多态的实现原理

多态的关键是在于通过基类指针或引用调用一个虚函数时,编译时不确定到底调用的是基类还是派生类的函数,运行时才确定—-这叫动态联编。“动态联编”是如何实现的呢?

多态实现的关键—虚函数表

每一个有虚函数的类(或虚函数的类的派生类)都有个虚函数表,该类的任何对象中都存放着虚函数表的指针。虚函数表中列出了该类的虚函数地址。多出来的4个字节是用来存放虚函数表的地址的。

#include <iostream>
using namespace std;
class A{
public:
    virtual void Func(){cout<<"A::Func()"<<endl;}
};
class B:public A{
public:
    virtual void Func(){cout<<"B::Func()"<<endl;}
};
int main(){
    A a;
    A *pa = new B();
    pa->Func();
    long long *p1 = (long long*) &a;
    long long *p2 = (long long*) pa;
    *p2 = *p1;//这里替换调了头8个字节,也就是替换了虚函数表的地址
    pa->Func();
    return 0;
}

虚析够函数

通过基类的指针删除派生类对象时,通常情况下只调用基类的析够函数

但是,删除一个派生类的对象时,应该先调用派生类的析够函数,然后调用基类的析够函数

解决办法:把基类的析够函数声明为virtual

派生类析够函数可以virtual不进行声明

通过基类的指针删除派生类对象时,首先调用派生类的析够函数,然后调用基类析够函数

一般来说,一个类如果定义了虚函数,则应该将析够函数也定义成虚函数。或者,一个类打算作为基类使用,也应该将析够函数定义成虚函数

不允许构造函数为虚函数

#include <iostream>
using namespace std;
class son{
public:
    virtual ~son(){cout<<"bye from son"<<endl;}
};
class grandson:public son{
public:
    ~grandson(){cout<<"bye from grandson"<<endl;}
};
int main()
{
    son *pson;
    pson = new grandson();
    delete pson;
    return 0;
}
bye from grandson
bye from son

纯虚函数和抽象类

纯虚函数:没有函数体的虚函数

virtual void Print()=0;

包含纯虚函数的类是抽象类

抽象类只能作为基类来派生新类使用,不能创建抽象类的对象

抽象类的指针和引用可以指向由抽象类派生出来的类的对象

在抽象类的成员函数可以调用纯虚函数,但是在构造函数或析够函数内部不能调用纯虚函数

如果一个类从抽象类派生而来,当且仅当它实现了基类中的所有纯虚函数,它才能够成为非抽象类

#include <iostream>
using namespace std;
class A{
public:
    virtual void f()=0;
    void g(){this->f();}
};
class B:public A{
public:
    void f(){cout<<"B:f()"<<endl;}
};
int main(){
    A* a = new B ();
    a->g();
    return 0;
}
B:f()

猜你喜欢

转载自blog.csdn.net/abc15766228491/article/details/80248813