C++繼承、多型,虛成員函式(包括虛解構函式、虛複製建構函式)學習筆記
阿新 • • 發佈:2019-02-04
通過哺乳類派生貓、狗等學習繼承、多型中的知識點
先貼上類的程式碼
#include<iostream> enum BREED { YORKIE, CAIRN, DANDIE, SHETLAND, DOBERAMN, LAB };//犬種 class Mammal { public: Mammal() :age(2), weight(5) {} //~Mammal() { std::cout << "Mamal 銷燬" << std::endl; }//解構函式 //如果基類Mammal沒有宣告虛解構函式,釋放派生類時將不能呼叫派生類的解構函式 virtual~Mammal() { std::cout << "Mamal 銷燬" << std::endl; }//虛解構函式 //虛複製建構函式 //通過基類指標建立一個派生類物件的拷貝 virtual Mammal* clone() { return new Mammal(*this); } int getAge()const { return age; } void setAge(int newAge) { age = newAge; } int getWeight()const { return weight; } void setWeight(int newWeight) { weight = newWeight; } //void speak() { std::cout << "Mamal Sound!" << std::endl; } virtual void speak()const { std::cout << "Mamal Sound!" << std::endl; }//虛擬函式 void move()const { std::cout << "Mamal moving..." << std::endl; } void sleep() { std::cout << "Mamal Sleeping!" << std::endl; } protected: int age; int weight; }; class Dog :public Mammal { public: Dog() :itsBreed(YORKIE) {} ~Dog() { std::cout << "Dog 銷燬" << std::endl; } //虛複製建構函式 //通過基類指標建立一個派生類物件的拷貝 virtual Mammal* clone() { return new Dog(*this); } BREED getBreed()const { return itsBreed; } void setBreed(BREED newBreed) { itsBreed = newBreed; } void speak()const { std::cout << "Woof!..." << std::endl; } void move()const { std::cout << "Dog moving..." << std::endl; } void wagTail() { std::cout << "Tail wagging..." << std::endl; } void begForFood() { std::cout << "Begging for food..." << std::endl; } protected: BREED itsBreed; }; //以下是簡化類 class Cat :public Mammal { public: ~Cat() { std::cout << "Cat 銷燬" << std::endl; } //虛複製建構函式 //通過基類指標建立一個派生類物件的拷貝 virtual Mammal* clone() { return new Cat(*this); } void speak()const { std::cout << "Meow!..." << std::endl; } void purr()const { std::cout << "Rrrrrrrrrrrrr..." << std::endl; }//Cat特有的方法 }; class Horse :public Mammal { public: ~Horse() { std::cout << "Horse 銷燬" << std::endl; } //如果沒有定義虛複製建構函式,將呼叫基類Mammal的方法,因為派生的Horse部分沒有複製過來 virtual Mammal* clone() { return new Horse(*this); } void speak()const { std::cout << "Whinny!..." << std::endl; } }; class Pig :public Mammal { public: ~Pig() { std::cout << "Pig 銷燬" << std::endl; } virtual Mammal* clone() { return new Pig(*this); } void speak()const { std::cout << "Oink!..." << std::endl; } };
主程式
#include <iostream> #include <string> #include "mammal.h" //呼叫speak方法 void valuefuction(Mammal mammalvalue);//切除,speak呼叫Mammal版本 void ptrfuction(Mammal *mammalvalue); void reffuction(Mammal &mammalvalue); int main() { Mammal *pDog = new Dog; pDog->speak();//pDog是Mammal物件,但是speak是虛成員函式,呼叫Dog中的方法 pDog->move();//pDog是Mammal物件,呼叫Mammal中的方法//dog沒有此方法 std::cout << "+++++++++++++++++++++++++Mammal指標陣列+++++++++++++++++++++++++" << std::endl; Mammal *array[5] = { new Dog,new Cat,new Horse,new Pig,new Mammal }; for (int i = 0; i < 5; i++) { //array[i]->speak(); (*array[i]).speak();//先解析再呼叫方法,與上一句同義 delete[]array[i]; //釋放指標陣列需迭代? } //delete[]array;//錯誤,釋放指標陣列需迭代? std::cout << "+++++++++++++++++++++++++Mammal切除+++++++++++++++++++++++++" << std::endl; //呼叫speak方法 Mammal *ptr = new Dog; valuefuction(*ptr);//切除,speak呼叫Mammal版本,因為引數為Mammal物件 ptrfuction(ptr); reffuction(*ptr); ptr = new Cat; valuefuction(*ptr);//切除,speak呼叫Mammal版本,因為引數為Mammal物件 ptrfuction(ptr); reffuction(*ptr); std::cout << "++++++驗證虛解構函式++++++++++" << std::endl; Mammal *Animal = new Mammal; Dog *pDog55 = new Dog; Mammal *horse = new Horse; delete horse;//如果基類Mammal沒有宣告虛解構函式,將不能呼叫Horse的解構函式 //可以通過註釋Horse類中的虛解構函式驗證 delete Animal; delete pDog55; std::cout << "++++++驗證虛解構函式+++++++++++++" << std::endl; std::cout << "++++++驗證虛複製建構函式+++++++++++++" << std::endl; Mammal *dog1 = new Dog; Mammal *horse1 = new Horse; dog1->speak(); horse1->speak(); Mammal *dog2 = dog1->clone(); Mammal *horse2 = (*horse1).clone(); dog2->speak();//Dog類定義了虛複製建構函式,可以正確呼叫Dog類的speak方法 horse2->speak();//Horse類如果沒有定義虛複製建構函式,將呼叫基類Mammal的方法,因為派生的Horse部分沒有複製過來 //註釋虛複製函式得到不同結果 std::cout << "++++++驗證虛複製建構函式+++++++++++++" << std::endl; std::cout << "++++++驗證dynamic_cast轉換 通過基類指標訪問派生類特定函式+++++++++++" << std::endl; Mammal *cat1 = new Cat; cat1->speak(); Cat* pcat = dynamic_cast<Cat*>(cat1); //cat1->purr();//錯誤,應該使用轉換得到的新指標呼叫 pcat->purr(); return 0; } void valuefuction(Mammal mammalvalue) { mammalvalue.speak(); } void ptrfuction(Mammal * mammalvalue) { mammalvalue->speak(); } void reffuction(Mammal & mammalvalue) { mammalvalue.speak(); }
筆記:
虛複製建構函式
通過基類指標建立一個派生類物件的拷貝
基類指標指向派生類物件,怎樣用基類指標建立一個新的派生類物件?
虛解構函式:當且僅當類裡包含至少一個虛擬函式的時候才去宣告虛解構函式。
如果釋放派生類時,基類沒有宣告虛解構函式,將不能呼叫派生類的解構函式
基類指標不能訪問派生類特有的成員函式,可用dynamic_cast轉換符運算子把基類指標轉換為派生類指標,如果轉換失敗指標為空
抽象類與具體類比較
1.抽象類不能直接例項化
2.抽象類可以不含抽象方法
3.抽象類中含有抽象方法時,如果子類繼承該抽象類,則必須重寫該抽象方法
4.抽象類可以有非抽象方法
5.允許(但不要求)抽象類包含抽象成員。
6.抽象類不能被密封。
7.抽象類含有非抽象方法,而且子類繼承該類時,重寫了該方法,那麼該子類的物件呼叫該方法時,是子類中的方法;如果子類沒有重寫該父類中的非抽象方法,那麼子類物件呼叫的是父類中的方法。