C++小結:多態(2) --類別與實現、動態多態

多      態

     1、動態多態的實現原理

    2、多態的類別及實現方法

    3、動態多態的四種情況

(2)多態的類別及實現方法

2.1 靜態多態:編譯時多態、函數的多態性,重載、模板、宏

2.2 動態多態:運行時多態、類的多態性,繼承和虛函數,晚綁定、動態綁定 

2.3 封裝可以使得代碼模塊化,繼承可以擴展已存在的代碼,他們的目的都是爲了代碼重用。而多態的目的則是爲了接口重用。也就是說,不論傳遞過來的究竟是哪個類的對象,函數都能夠通過同一個接口調用到適應各自對象的實現方法。

(3)動態多態的四種情況

3.1 用一個父類的指針指向一個子類對象

3.2 用一個父類的指針當函數的形參,用這個指針可以接受到任何它的子類對象也包括他自己

3.3 在複合類,儘量飲用高層次的類(父類的指針)當做類的成員變量,這樣就可以通過它創建出它所對應的任何子類對象包括他自己

3.4 在容器中,可以聲明一個父類指針的容器,這時可以往容器中添加它所對應的任何子類對象包括他自己

動態多態和靜態多態的比較
靜態多態
優點:
由於靜多態是在編譯期完成的,因此效率較高,編譯器也可以進行優化;
有很強的適配性和鬆耦合性,比如可以通過偏特化、全特化來處理特殊類型;
最重要一點是靜態多態通過模板編程爲C++帶來了泛型設計的概念,比如強大的STL庫。
缺點:
由於是模板來實現靜態多態,因此模板的不足也就是靜多態的劣勢,比如調試困難、編譯耗時、代碼膨脹、編譯器支持的兼容性
不能夠處理異質對象集合
動態多態
優點:
OO設計,對是客觀世界的直覺認識;
實現與接口分離,可複用
處理同一繼承體系下異質對象集合的強大威力
缺點:
運行期綁定,導致一定程度的運行時開銷;
編譯器無法對虛函數進行優化
笨重的類繼承體系,對接口的修改影響整個類層次;
不同點:
本質不同,靜態多態在編譯期決定,由模板具現完成,而動態多態在運行期決定,由繼承、虛函數實現;
動態多態中接口是顯式的,以函數簽名爲中心,多態通過虛函數在運行期實現,靜態多臺中接口是隱式的,以有效表達式爲中心,多態通過模板具現在編譯期完成
相同點:
都能夠實現多態性,靜態多態/編譯期多態、動態多態/運行期多態;
都能夠使接口和實現相分離,一個是模板定義接口,類型參數定義實現,一個是基類虛函數定義接口,繼承類負責實現

//這裏隱藏是指派生類的函數屏蔽了與其同名的基類函數,規則如下:

//1)如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)。

//2)如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。

//上面的程序中:

//1)函數Derived::f1(float)覆蓋了Base::f1(float)

//2)函數Derived::f2(int)隱藏了Base::f2(float),而不是重載。

//3)函數Derived::f3(float)隱藏了Base::f3(float),而不是覆蓋


A、動態多態
#ifndef __C__No807Class__Father__
#define __C__No807Class__Father__

#include <iostream>
using namespace std;
class Father
{
public:
    virtual void print();
};
#endif /* defined(__C__No807Class__Father__) */

#include "Father.h"
void Father::print()
{
    cout << "Father print" << endl;
}

#ifndef __C__No807Class__Son__
#define __C__No807Class__Son__

#include <iostream>
#include "Father.h"
class Son : public Father
{
public:
    void print();
};
#endif /* defined(__C__No807Class__Son__) */

#include "Son.h"
void Son::print()
{
    cout << "Son print" << endl;
}

#ifndef __C__No807Class__Son2__
#define __C__No807Class__Son2__

#include <iostream>
#include "Father.h"
class Son2 : public Father
{
public:
    void print();
};

#endif /* defined(__C__No807Class__Son2__) */

#include "Son2.h"
void Son2::print()
{
    cout << "Son2 print" << endl;
}

#include <iostream>
#include<string>
#include<vector>
#include "Son.h"
#include "Son2.h"
//如果容器中函數的對象是不同類的,那麼這時候用使用多態是非常的方便的
void func(const vector<Father*>&V)  //容器中,可以聲明一個父類指針的容器,可以往這個容器中添加它所對應的任何子類對象包括他自己
{
    for(int i = 0; i < V.size(); ++i)
    {
        V.at(i) -> print();
    }
}
void foo(Father &f)  //父類指針當函數形參,用這個指針可以接受到任何它的子類對象也包括他自己
{
    f.print();  //Son,不加引用Father
}

int main()
{
    Son s;
    Son2 s2;
    Father f;
    Father *p;
    p = &s;    //父類指針指向子類對象
    p -> print();
    p = &s2;
    p -> print();
    foo(s);
    vector<Father*>FatherVector;
    FatherVector.push_back(&s);
    FatherVector.push_back(&s2);
    FatherVector.push_back(&f);
    func(FatherVector);  
    return 0;
}

B、靜態多態--重載
函數重載,使用相同的函數名,但是函數參數列表不同,(參數類型或個數),用這種方法來實現多態。函數重載和模板就多態來講其實是一樣的,只不過是重載函數要寫多個函數,而模板的話我們使用typedef,只要寫一個函數,在調用的時候,會根據參數的類型自己去替換匹配而已。
#include <iostream>
#include <string>
using namespace std;
int add (int num1, int num2)
{
    return (num1 + num2);
}
int add (int num1, string str1)
{
    return (num1 + atoi(str1.c_str()));  //把字符串的數字轉爲數字型的數值
}
int add (int num1, char c1)
{
    return (num1 + c1);
}
int main()
{
    int n1 = 10, n2 = 20;
    string s1 = "30";
    char c1 = 'a';
    cout << n1 << " + " << n2 << " = " << add(n1, n2) <<endl;
    cout << n1 << " + " << s1 << " = " << add(n1, s1) <<endl;
    cout << n1 << " + " << c1 << " = " << add(n1, c1) <<endl;
    return 0;
}

C、靜態多態--模板
template <typename T>
T max(const T& i, const T& j) 
{
       return (i > j) ? i : j;
}
//返回兩個任意類型對象的最大值(對象),前提是該類型能夠使用>運算符進行比較,並且返回值是bool類型。
使用:
int a = 3; int b = 4;
cout << max(a, b) << endl;
float c = 2.4; float d = 1.2;
cout << max(c, d) << endl;
輸出結果爲:
         4
        2.4
這種綁定發生在編譯期,這是由於模板的實例化是發生在編譯期的,即在編譯時編譯器發現你調用max(a, b)時就自動生成一個函數
int max(const int& i, const int& j) 
{
       return (i > j) ? i : j;
}
即將所有的T替換成int;
當你調用max(c, d)時就自動生成一個函數
float max(const float& i, const float& j)
{
        return (i > j) ? i : j;
}
模板2
#include<iostream>
#include<string>
#include<vector>
using namespace std;
/*虛函數機制*/
class Base
{
public:
    virtual void print()=0;
};

class Child1:public Base
{
    void print()
    {
        cout<<"我是Child1類"<<endl;
    }
};
class Child2:public Base
{
    void print()
    {
        cout<<"我是Child2類"<<endl;
    }
};
/*如果容器中函數的對象是不同類的,那麼這時候用使用多態是非常的方便的*/
void func(const vector<Base*>&V)
{
    for(int i=0;i<V.size();++i)
    {
        V.at(i)->print();
    }
}

/*靜態多態,使用模板*/
template<typename T>
void TempalteFuncion(const vector<T*>&V)
{
    for(int i=0;i<V.size();++i)
    {
        V.at(i)->print();
    }
}
int main()
{
    /*虛函數多態  模板多態混用*/
    Child1 child1;
    Child2 child2;
    Base *p;
    p=&child1;
    p->print();
    p=&child2;
    p->print();
    vector<Base*>BaseVector;
    BaseVector.push_back(&child1);
    BaseVector.push_back(&child2);
    func(BaseVector);
    TempalteFuncion(BaseVector);
    
    return 0;
}
D、靜態多態--宏多態(忽略)
#include<iostream>  
#include<string>  
using namespace std;  
  
#define ADD(A,B) ((A)+(B))  
  
void main()  
{  
    /*宏多態*/  
    int num1=10;  
    int num2=20;  
    string str1="22";  
    string str2="33";  
    cout<<"宏多態 ADD(A+B) A+B:"<<ADD(num1,num2)<<endl;  
    cout<<"宏多態 ADD(A+B) A+B:"<<ADD(str1,str2)<<endl;  
}  

E、子類指針指向父類對象、隱藏
#include <iostream>
using namespace std;
class A
{
public:
    void f1()
    {
        cout << "1" << endl;
    }
    virtual void f2()
    {
        cout << "2" << endl;
    }
};
class B : public A
{
public:
    void f1()
    {
        cout << "3" << endl;
    }
    void f2()
    {
        cout << "4" << endl;
    }
};
int main()
{
    A a;
    B b;
    A *p = &a;
    p -> f1();  //1
    p -> f2();  //2
    p = &b;
    p -> f1();  //1
    p -> f2();  //4
    B *ptr = (B *)&a;  //子類指針ptr,父類對象a的引用強制轉化爲B類類型指針
    ptr -> f1();  //3
    ptr -> f2();  //2
    
    return 0;
}
#include <iostream>
using namespace std;
class Base
{
public:
    virtual void f1(float x)
    {
        cout << "Base f1 (float) = " << x << endl;
    }
    void f2(float x)
    {
        cout << "Base f2 (float) = " << x << endl;
        }
    void f3(float x)
    {
        cout << "Base f3 (float) = " << x << endl;
    }
};
class Derived : public Base
{
public:
    void f1(float x)
    {
        cout << "Derived f1 (float) = " << x << endl;  //覆蓋,多態
    }
    void f2(int x)
    {
        cout << "Derived f2 (float) = " << x << endl;  //隱藏
    }
    void f3(float x)
    {
        cout << "Derived f3 (float) = " << x << endl;  //隱藏
    }
};
int main()
{
    Derived d;
    Base *pb = &d;
    Derived *pd = &d;
    
    pb -> f1(3.14f);  //D f1
    pd -> f1(3.14f);  //D f1
    
    pb -> f2(3.14f);  //B f2
    pd -> f2(3.14f);  //D f2
    
    pb -> f3(3.14f);  //B f3
    pd -> f3(3.14f);  //D f3
    
    return 0;
}
//這裏“隱藏”是指派生類的函數屏蔽了與其同名的基類函數,規則如下:
//(1)如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual 關鍵字,基類的函數將被隱藏(注意別與重載混淆)。
//(2)如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual 關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。
//上面的程序中:
//(1)函數Derived::f1(float)覆蓋了Base::f1(float)。
//(2)函數Derived::f2(int)隱藏了Base::f2(float),而不是重載。
//(3)函數Derived::f3(float)隱藏了Base::f3(float),而不是覆蓋

發佈了31 篇原創文章 · 獲贊 3 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章