1. 程式人生 > >C++繼承、多型,虛成員函式(包括虛解構函式、虛複製建構函式)學習筆記

C++繼承、多型,虛成員函式(包括虛解構函式、虛複製建構函式)學習筆記

通過哺乳類派生貓、狗等學習繼承、多型中的知識點

先貼上類的程式碼

#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.抽象類含有非抽象方法,而且子類繼承該類時,重寫了該方法,那麼該子類的物件呼叫該方法時,是子類中的方法;如果子類沒有重寫該父類中的非抽象方法,那麼子類物件呼叫的是父類中的方法。