多态与虚函数

多态是指同一名字的事物可以完成不同的功能。多态可以分为编译时的多态和运行时
  的多态。前者主要是指函数重载、对重载函数的调用,在编译时就能根据实参确定应
  该调用哪个函数。后者则和继承、虚函数等概念相关。
 
     通过基类指针实现多态:
      派生类对象的地址可以赋值给基类指针。对于通过基类指针调用基类和派生类中
   都有的同名、同参数表的虚函数语句,编译时并不确定要执行的是基类还是派生
   类的虚函数,而当程序运行到该语句时,如果基类指针指向一个基类对象,则基
   类的虚函数被调用,如果基类的指针指向一个派生类对象,则派生类虚函数被调
   用。这种机制就叫做多态。
  
  通过基类引用实现多态:
         通过基类的引用调用虚函数的语句也是多态的。
  
 虚函数:
     在函数声明前加上virtual关键字的成员函数。virtual关键字只在类定义中的成员函
  数声明处使用,不能再类外部写成员函数体时使用。
  
  静态成员函数不能是虚函数。
  包含虚函数的类称为多态类。
  
 多态的作用:
     在面向对象的程序设计中,使用多态能够增加程序的可扩充性,即程序需要修改或增加功能
  时,只需改动或增加较少的代码。此外,使用多态也能够起到精简代码的作用。

#include <iostream>
#include <string>

using namespace std;

class fruit{
    private:
        double price;
    public:
        virtual void Print(){
            cout << "Base Class" << endl;
        }
}; 

class apple:public fruit{
    private:
        string area;
    public:
        virtual void Print(){
            cout << "Derived Class" << endl;
        } 
};

int main()
{
    fruit f;
    apple a;
    fruit *pf1 = &f;
    fruit *pf2 = &a;
    fruit &pf3 = a; 
    pf1->Print();
    pf2->Print();
    pf3.Print(); 
    
    return 0;
}

/*
    输出结果:
        Base Class
        Derived Class
        Derived Class 
*/ 

关于多态的主意事项:
     类的成员函数之间而已互相调用。在成员函数(静态成员函数、构造函数和析构函数除外)中
  调用其它虚成员函数的语句时多态的。

#include <iostream>

using namespace std;

class fruit{
    public:
        void fun(){
            Print();
        } 
        virtual void Print(){
            cout << "Base Class" << endl;
        }
}; 

class apple:public fruit{
    public:
        virtual void Print(){
            cout << "Derived Class" << endl;
        } 
};

int main()
{
    apple a;
    a.fun();   // 输出:Derived Class 
    // 由于a是一个apple类对象,所以调用的是apple中的Print函数 
    
    return 0;
}

在构造函数和析构函数中调用虚函数:
     在构造函数和析构函数中调用虚函数不是多态,因为编译时即可确定调用的是哪个函
  数。如果本类中有该函数,调用的就是本类中的函数;如果本类中没有,调用的就是
  直接基类的函数;如果直接基类中没有,就调用间接基类中的,以此类推。
 
 注意区分多态和非多态的情况:
     要注意,通过基类指针或引用调用成员函数的语句,只有当该成员函数时虚函数时才
  会是多态。如果该成员函数不是虚函数,那么这条函数调用语句就是静态联编的,编
  译时就能确定调用的时哪个类的成员函数。
  
 C++规定,只要基类中某个函数被声明为虚函数,则派生类中的同名、同参数表的成员函数
 即使前面不加virtual关键字,也自动成为虚函数。

 

虚析构函数:
       有时我们会将基类指针指向一个用new运算符动态生成的派生类对象,在释放该对象时是
    通过释放指向它的指针实现的。这时,由于delete语句时静态编联的,编译器在此处无
    法此时这个指针是指向哪个对象的,只会根据指针是基类类型,而调用基类的析构函数,
    从而造成程序的问题。这时,我们可以通过将基类的析构函数声明为虚函数来解决这个
    问题。

#include <iostream>

using namespace std;

class fruit1{
    public:
        ~fruit1(){
            cout << "Base Class" << endl;
        }
}; 

class apple1:public fruit1{
    public:
        ~apple1(){
            cout << "Derived Class" << endl;
        } 
};

class fruit2{
    public:
        virtual ~fruit2(){
            cout << "Base Class" << endl;
        }
}; 

class apple2:public fruit2{
    public:
        virtual ~apple2(){
            cout << "Derived Class" << endl;
        } 
};

int main()
{
    fruit1 *pf = new apple1;
    delete pf;    
    fruit2 *p_f = new apple2;
    delete p_f; 
    
    return 0;
}

/*
    输出结果:
        Base Class
        Derived Class
        Base Class 
*/ 

 纯虚函数和抽象类:
     纯虚函数就是没有函数体的虚函数。包含纯虚函数的类就叫做抽象类。
  纯虚函数的写法就是在函数声明后加"=0"
  
 抽象类不能独立生成对象。
 
 抽象类的作用:
     抽象类可以作为基类,用来派生新类。可以定义抽象类的指针或引用,并让它们指向或引
  用抽象类的派生类的对象,为多态的实现提供条件。独立的抽象类的对象不存在,但是包
  含在派生类对象中的抽象类的对象是可以存在的。
  
 如果一个类从抽象类派生而来,当且仅当它对基类中的所有纯虚函数都进行覆盖并都写出函数
 体,它才能成为非抽象类。

class fruit1{                     // 抽象类 
    public:
        virtual void Print() =0;  // 纯虚函数 
}; 

 

posted @ 2017-10-08 16:58  荒唐了年少  阅读(406)  评论(0编辑  收藏  举报