C++ 一些學習筆記(十二)類和物件-多型

haloo__發表於2020-10-08

主要是針對之前學習C的時候一些知識點的遺漏的補充,還有一些我自己覺得比較重要的地方。

本文章的主要內容是關於多型。

多型C++是物件導向三大特性之一。
多型的優點:

  • 組織結構清晰
  • 可讀性強
  • 對於前期和後期的擴充套件以及維護性高

1.多型的基本概念

多型分為兩類

  • 靜態多型:函式過載和運算子過載屬於靜態多型,複用函式名。
    靜態多型的函式地址早繫結-編譯階段確定函式地址

  • 動態多型:派生類和虛擬函式實現執行時多型。
    動態多型的函式地址晚繫結-執行階段確定函式地址

動態多型的滿足條件:
1.有繼承關係
2.子類要重寫父類的虛擬函式(重寫 函式返回值型別、函式名、引數列表完全相同

動態多型的使用:父類的指標或者引用指向子類物件

class Animal{
public:
	virtual void speak(){//虛擬函式
	
		cout<<"動物在說話"<<endl;
	}
};
class Cat:public Animal{
public:
	void speak(){
		cout<<"小貓在說話"<<endl;
	}
}class Dog:public Animal{
public:
	void speak(){
		cout<<"小狗在說話"<<endl;
	}
}//執行doSpeak函式,地址早繫結,編譯階段確定函式地址
//如果想讓貓說話,那麼這個函式地址就不能提前繫結,需要在執行階段進行繫結,地址晚繫結
void doSpeak(Animal &animal){//Animal &animal=cat 父類引用指向子類的物件,C++中允許父子之間的型別轉換,不需要做強制型別轉換,父類的引用/指標可以直接指向子類物件
	animal.speak;
}
void test01(){
	Cat cat;
	doSpeak(cat);
	Dog dog;
	doSpeak(dog);
}
int main(){
	test01();
}



動態多型的滿足條件:
1.有繼承關係
2.子類要重寫父類的虛擬函式(重寫 函式返回值型別、函式名、引數列表完全相同

動態多型的使用:父類的指標或者引用指向子類物件

2.多型的原理剖析

在這裡插入圖片描述

//多型實現計算器
#include<iostream>
#include <string>
using namespace std;
class Calculator{
public:
    int getResult(string oper){
        if (oper=="+") {
            return m_Num1+m_Num2;
        }
        else if (oper=="-"){
            return m_Num1-m_Num2;
        }
        else if (oper=="*"){
            return m_Num1*m_Num2;
        }
        else return 0;
    }
    int m_Num1;
    int m_Num2;
};
void test01(){
    Calculator c;
    c.m_Num1=10;
    c.m_Num2=10;
    cout<<c.m_Num1<<"+"<<c.m_Num2<<"="<<c.getResult("+")<<endl;
    cout<<c.m_Num1<<"-"<<c.m_Num2<<"="<<c.getResult("-")<<endl;
    cout<<c.m_Num1<<"*"<<c.m_Num2<<"="<<c.getResult("*")<<endl;
}

//利用多型實現計算器
//實現計算器抽象類
class AbstractCalculator{
public:
    virtual int getResult(){
        return 0;
    }
    
    int m_Num1;
    int m_Num2;
};
//加法計算器類
class AddCalculator:public AbstractCalculator{
public:
    int getResult(){
        return m_Num1+m_Num2;
    }
};
//減法計算器類
class SubCalculator:public AbstractCalculator{
public:
    int getResult(){
        return m_Num1-m_Num2;
    }
};
//乘法計算器類
class MulCalculator:public AbstractCalculator{
public:
    int getResult(){
        return m_Num1*m_Num2;
    }
};

//除法計算器類
class ChuCalculator:public AbstractCalculator{
public:
    int getResult(){
        return m_Num1/m_Num2;
    }
};

void test02(){
//多型的引用條件
//父類指標或引用指向子類物件
    //加法運算
    AbstractCalculator*abc=new AddCalculator;
    abc->m_Num1=100;
    abc->m_Num2=100;
    cout<<abc->m_Num1<<"+"<<abc->m_Num2<<"="<<abc->getResult()<<endl;
    delete abc;//用完記得銷燬
    
    //減法運算
    abc=new SubCalculator;
    abc->m_Num1=100;
    abc->m_Num2=100;
    cout<<abc->m_Num1<<"-"<<abc->m_Num2<<"="<<abc->getResult()<<endl;
    delete abc;//用完記得銷燬
    
    //乘法法運算
    abc=new MulCalculator;
    abc->m_Num1=100;
    abc->m_Num2=100;
    cout<<abc->m_Num1<<"*"<<abc->m_Num2<<"="<<abc->getResult()<<endl;
    delete abc;//用完記得銷燬
    
    //整除運算
    abc=new ChuCalculator;
    abc->m_Num1=100;
    abc->m_Num2=100;
    cout<<abc->m_Num1<<"/"<<abc->m_Num2<<"="<<abc->getResult()<<endl;
    delete abc;//用完記得銷燬
}

int main(){
    //test01();
    test02();
    system("pause");
    return 0;
}

3.純虛擬函式和抽象類

在多型中,通常父類中虛擬函式的實現是毫無意義的,主要都是呼叫子類重寫的內容,因此可以將虛擬函式改寫為純虛擬函式
純虛擬函式的語法:virtual 返回值型別 函式名 (引數列表)=0
當類中有了純虛擬函式,這個類也稱為抽象類
抽象類的特點:

  • 無法例項化物件
  • 子類必須重寫抽象類中的純虛擬函式,否則也屬於抽象類
#include<iostream>
#include <string>
using namespace std;
class Base{
public:
    virtual void func()=0;
};
class Son:public Base{
public:
    virtual void func(){
        cout<<"func函式呼叫"<<endl;
    }
};
void test01(){
    //Son s;
    Base*base=new Son;
    base->func();
}
int main(){
    test01();
    system("pause");
    return 0;
}

一個例子

#include<iostream>
#include <string>
using namespace std;
class AbstractDrinking{
public:
    virtual void Boil()=0;
    virtual void Brew()=0;
    virtual void PourInCup()=0;
    virtual void PutSomething()=0;
    void makeDrink(){
        Boil();
        Brew();
        PourInCup();
        PutSomething();
    }
};
class Coffee:public AbstractDrinking{
    virtual void Boil(){
        cout<<"煮水"<<endl;
    }
    virtual void Brew(){
        cout<<"沖泡咖啡"<<endl;
    }
    virtual void PourInCup(){
        cout<<"倒入杯中"<<endl;
    }
    virtual void PutSomething(){
        cout<<"加入糖和牛奶"<<endl;
    }
};
class Tea:public AbstractDrinking{
    virtual void Boil(){
        cout<<"煮礦泉水"<<endl;
    }
    virtual void Brew(){
        cout<<"沖泡茶葉"<<endl;
    }
    virtual void PourInCup(){
        cout<<"倒入杯中"<<endl;
    }
    virtual void PutSomething(){
        cout<<"加入檸檬"<<endl;
    }
};
void doWork(AbstractDrinking*abs){//父類指標指向子類物件
    abs->makeDrink();
    delete abs;
}
void test01(){
    doWork(new Coffee);
    cout<<"______________"<<endl;
    doWork(new Tea);
}
int main(){
    test01();
    system("pause");
    return 0;
}

4.虛析構和純虛析構

問題:多型使用時,如果子類中有屬性開闢到堆區,那麼父類指標在釋放時無法呼叫子類的析構程式碼。
解決方法:將父類中的解構函式改為虛析構或者純虛擬函式

虛析構和純虛析構共性:

  • 可以解決父類指標釋放子類物件
  • 都需要有具體的函式實現

虛析構和純虛析構區別:

  • 如果是純虛析構,該類屬於抽象類,無法例項化物件

虛析構語法:virtual ~類名(){}
純虛析構語法:宣告virtual ~類名(){}=0;,實現類名::~(){}

純虛析構需要在類內宣告,在類外實現

#include<iostream>
#include <string>
using namespace std;
class Animal{
public:
    Animal(){
        cout<<"Animal建構函式呼叫"<<endl;
    }
//    virtual ~Animal(){//虛析構,利用虛析構可以解決父類指標釋放子類物件時不乾淨的問題
//        cout<<"Animal解構函式呼叫"<<endl;
//    }
    virtual ~Animal()=0;//純虛析構
    virtual void speak()=0;//純虛擬函式
};
Animal::~Animal(){
    cout<<"Animal純解構函式呼叫"<<endl;
}//純虛析構

class Cat:public Animal{
public:
    Cat(string name){
        cout<<"Cat建構函式呼叫"<<endl;
        m_Name=new string(name);
    }
    ~Cat(){
        if (m_Name!=NULL) {
            cout<<"Cat解構函式呼叫"<<endl;
            delete m_Name;
            m_Name=NULL;
        }
    }
    virtual void speak(){
        cout<<*m_Name<<"小貓在說話"<<endl;
    }
    string *m_Name;
};

void test01(){
    Animal*animal=new Cat("Tom");
    animal->speak();//父類指標在析構時,不會呼叫子類中解構函式,導致子類如果有堆區屬性,出現記憶體洩漏
    delete animal;//
}
int main(){
    test01();
    system("pause");
    return 0;
}

1.虛析構或純虛析構就是用來解決通過父類指標釋放子類物件的問題
2.如果子類中沒有堆區資料,可以不寫虛析構或虛構函式
3,擁有純虛解構函式的類也屬於抽象類

5.電腦組裝案例

#include<iostream>
#include <string>
using namespace std;
class CPU{
public:
    virtual void caculate()=0;
};
class VideoCard{
public:
    virtual void display()=0;
};
class Memory{
public:
    virtual void storage()=0;
};
class Computer{
public:
    Computer(CPU*cpu,VideoCard*vc,Memory*mem){
        m_cpu=cpu;
        m_vc=vc;
        m_mem=mem;
    }
    ~Computer(){
        if (m_cpu!=NULL) {
            delete m_cpu;
            m_cpu=NULL;
        }
        if (m_vc!=NULL) {
            delete m_vc;
            m_vc=NULL;
        }
        if (m_mem!=NULL) {
            delete m_mem;
            m_mem=NULL;
        }
    }
    //提供工作的函式
    void work(){
        m_cpu->caculate();
        m_vc->display();
        m_mem->storage();
    }
private:
    CPU*m_cpu;//cpu的零件指標
    VideoCard*m_vc;//顯示卡零件指標
    Memory*m_mem;//記憶體條零件指標
};

class IntelCPU:public CPU{
  public:
    virtual void caculate(){
        cout<<"Intel的CPU開始計算了!"<<endl;
    }
};
class IntelVideoCard:public VideoCard{
  public:
    virtual void display(){
        cout<<"Intel的顯示卡開始顯示了!"<<endl;
    }
};
class IntelMemory:public Memory{
  public:
    virtual void storage(){
        cout<<"Intel的記憶體條開始儲存了!"<<endl;
    }
};

class LenovoCPU:public CPU{
  public:
    virtual void caculate(){
        cout<<"Lenovo的CPU開始計算了!"<<endl;
    }
};
class LenovoVideoCard:public VideoCard{
  public:
    virtual void display(){
        cout<<"Lenovo的顯示卡開始顯示了!"<<endl;
    }
};
class LenovoMemory:public Memory{
  public:
    virtual void storage(){
        cout<<"Lenovo的記憶體條開始儲存了!"<<endl;
    }
};


void test01(){
    //第一臺電腦零件
    CPU*intelCpu=new IntelCPU;
    VideoCard*intelCard=new IntelVideoCard;
    Memory*intelMem=new IntelMemory;
    
    //建立第一臺電腦
    Computer*computer1=new Computer(intelCpu,intelCard,intelMem);
    computer1->work();
    delete computer1;
    
    cout<<"--------------------"<<endl;
    
    //建立第二臺電腦
    Computer*computer2=new Computer(new LenovoCPU,new LenovoVideoCard,new LenovoMemory);
    computer2->work();
    delete computer2;
    
    //建立第三臺電腦
    cout<<"--------------------"<<endl;
    Computer*computer3=new Computer(new LenovoCPU,new IntelVideoCard,new LenovoMemory);
    computer3->work();
    delete computer3;
}
int main(){
    test01();
    system("pause");
    return 0;
}

相關文章