C++之多態與虛函數的實例演示

此實例演示能帶你貫穿C++面向對象的抽象、封裝、繼承和多態性的知識點,還考察了對運算符重載、友元等知識點的運用。

共用頭文件

#ifndef _GLOBAL_H
#define _GLOBAL_H
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
#include <cmath>
#include <queue>
using namespace std;
#pragma warning(disable:4996)
#endif

基類定義

#ifndef _Sales_data_h   //防止嵌套包含頭文件
#define _Sales_data_h
#include "GLOBAL.h"
template<typename T>
class Base_data {
public:
	Base_data(int _count) :count(_count) { cout << "Base_data:" << &(*this) << endl; }
	Base_data() { cout << "Base_data:"<<&(*this) << endl; }
	virtual ~Base_data(){ 
		if(this)
			cout << "~Base_data:" << &(*this) << endl;
	}
	Base_data(const Base_data<T>& obj) { cout << "Base_data:" << &(*this) << endl; }
	/*派生類中要重定義基類虛函數,要注意參數必須爲基類引用類型,
	否則與基類中虛函數是完全不同的,無法進行預期的動態綁定。
	參考https://blog.csdn.net/gettogetto/article/details/52416463
	*/
	//純虛賦值函數,純虛函數必須被派生類實現,否則會編譯報錯
	virtual Base_data<T>& operator =(Base_data<T>& obj) = 0; 

	friend ostream& operator<<(ostream& output, const Base_data<T>& obj) { 
		obj.print(output);
		return output;
	}
	friend istream& operator>>(istream& input, Base_data<T>& obj) { return input; }
protected:
	/*如果想爲基類的層次結構提供輸出功能,則可以爲friend ostream& operator<<提供一個可以被調用的virtual void print
	參考https://stackoverflow.com/questions/4571611/making-operator-virtual
	*/
	virtual void print(ostream& output) const = 0;

	
private:
	int count;
};
#endif // !_Sales_data_h

派生類定義

#include "Sales_data.h"
template<typename T>
//虛基類的方法使得在繼承間接共同基類時只保留一份成員
class Sales_data:virtual public Base_data<T> 
{ 
	
public:
	//默認構造函數
	Sales_data():bookNo(""), units_sold(0), revenue(0),Base_data(0)
	{ 
		cout << "Sales_data:" << &(*this) << endl; 
	}
	//拷貝構造函數
	Sales_data(Sales_data<T>& obj) 
	{
		this->bookNo = obj.bookNo;
		this->units_sold = obj.units_sold;
		this->revenue = obj.revenue;
		cout << "Sales_data:" << &(*this) << endl;
	}
	//帶參構造函數
	Sales_data(string bookNo, unsigned unist_sold, T revenue) {
		this->bookNo = bookNo;
		this->units_sold = unist_sold;
		this->revenue = revenue;
		cout << "Sales_data:" << &(*this) << endl;
	}
	//析構函數
	~Sales_data() {
		if (this)
			cout << "~Sales_data:"<<&(*this) << endl;
	}

	/*
	[C++]虛函數-同名訪問
	https://www.cnblogs.com/Rosanna/p/3346046.html
	dynamic_cast與繼承層次的指針
	對於“向下轉型”有兩種情況。
	一種是基類指針所指對象是派生類類型的,這種轉換是安全的;
	另一種是基類指針所指對象爲基類類型,在這種情況下dynamic_cast在運行時做檢查,轉換失敗,返回結果爲0;
	https://www.cnblogs.com/xiangtingshen/p/10851851.html
	*/
	/*
	virtual Base_data<T>& operator =(Base_data<T>& obj) 返回值選擇Base_data<T>&或Sales_data<T>&都可以,選擇Base_data<T>&就涉及到“向上轉型”,即是派生類指針或引用類型(隱式)轉換爲其基類類型,本身就是安全的,沒必要使用dynamic_cast,普通轉換就可以達到目的,畢竟使用dynamic_cast是需要開銷的。
	*/
	/*
	在派生類中重定義基類的虛函數,函數名、函數類型、函數參數個數和類型必須與基類的虛			   函數相同,根據派生類的需要重新定義函數體。
	當一個成員函數被聲明爲虛函數後,其派生類中的同名函數都自動成爲虛函數。因此在派生類重新聲明該虛函數時,可以加virtual,也可以不加,但習慣上一般在每一層聲明該函數時都加virtual,使程序更加清晰。
	*/
	virtual Base_data<T>& operator =(Base_data<T>& obj)
	{
		Sales_data<T>* temp = dynamic_cast<Sales_data<T>*>(&obj); //安全的向下轉型,把基類指針轉換成派生類指針
		if (this != &obj && temp)
		{
			this->bookNo = temp->bookNo;
			this->units_sold = temp->units_sold;
			this->revenue = temp->revenue;
		}
		return *this;
	}
	
	//運算符重載,普通賦值函數
	Sales_data<T>& operator =(Sales_data<T>& obj) {
		if (this != &obj)
		{
			this->bookNo = obj.bookNo;
			this->units_sold = obj.units_sold;
			this->revenue = obj.revenue;
		}
		return *this;
	}

	/*
	友元不是成員函數,但是它可以訪問類中的私有成員。友元的作用在於提高程序的運行效率,
	但是,它破壞了類的封裝性和隱藏性,使得非成員函數可以訪問類的私有成員。
	*/
	friend ostream& print(ostream& output, const Sales_data<T>& obj) {
		return output << "bookNo:" << obj.bookNo << ",units_sold:" << obj.units_sold << ",revenue:" << obj.revenue << endl;
	}


	friend istream& read(istream& input, Sales_data<T>& obj)
	{
		T price = 0;
		input >> obj.bookNo >> obj.units_sold >> price;
		obj.revenue = obj.units_sold * price;
		return input;
	}
	
	friend ostream& operator<<(ostream& output, const Sales_data<T>& obj)
	{
		return output <<"id:"<<&obj<< ",bookNo:" << obj.bookNo << ",units_sold:" << obj.units_sold << ",revenue:" << obj.revenue << endl;
	}

	friend istream& operator>>(istream& input, Sales_data<T>& obj)
	{
		T price = 0;
		input >> obj.bookNo >> obj.units_sold >> price;
		obj.revenue = obj.units_sold * price;
		return input;
	}

protected:
	//覆蓋/重寫基類定義的print方法
	virtual void print(ostream& output) const {
		output << "id:" << &(*this) << ",bookNo:" << this->bookNo << ",units_sold:" << this->units_sold << ",revenue:" << this->revenue << endl;
	}

private:
	string bookNo;
	unsigned units_sold;
	T revenue;
};
int main()
{
{
		Sales_data<double>* objList[] = {
			new Sales_data<double>("C++", 2, 2.5),
			new Sales_data<double>("C++", 2, 2.5),
			new Sales_data<double>("C++", 2, 2.5),
			new Sales_data<double>("C++", 2, 2.5),
			new Sales_data<double>("C++", 2, 2.5)
		};
		/*
		當子類重新定義了父類的虛函數後,父類指針根據賦給它的不同的子類指針,動態地調用屬於子類的該函數,這樣的函數調用在編譯期間是無法確定的(調用的子類的虛函數的地址無法給出)。因此,這樣的函數地址是在運行期間綁定的(晚綁定)。
		*/
		for each (Base_data<double>* var in objList) //向上轉型達到多態的目的	
		{
			cout << *var;
			delete var;
		}
		Base_data<double> *obj1 = new Sales_data<double>("C++", 2, 2.5);//調用帶參構造函數
		Sales_data<double> obj2("C", 20, 1.5); 
		Sales_data<double> *obj3 = new Sales_data<double>("C#", 10, 2.5); //調用帶參構造函數
		*obj1 = obj2;  //重載賦值函數
		cout << *obj1;
		cout << obj2;
		cout << *obj3;
		delete obj1; //析構函數
		delete obj3;//析構函數
	}	//析構函數
	system("pause");
	return 0;
}
id:000001936AE05000,bookNo:C++,units_sold:2,revenue:2.5
~Sales_data:000001936AE05000
~Base_data:000001936AE05048
id:000001936AE03830,bookNo:C++,units_sold:2,revenue:2.5
~Sales_data:000001936AE03830
~Base_data:000001936AE03878
id:000001936AE140B0,bookNo:C++,units_sold:2,revenue:2.5
~Sales_data:000001936AE140B0
~Base_data:000001936AE140F8
Base_data:000001936AE052F8
Sales_data:000001936AE052B0
Base_data:000000547A36EF98
Sales_data:000000547A36EF50
Base_data:000001936AE03878
Sales_data:000001936AE03830
id:000001936AE052B0,bookNo:C,units_sold:20,revenue:1.5
id:000000547A36EF50,bookNo:C,units_sold:20,revenue:1.5
id:000001936AE03830,bookNo:C#,units_sold:10,revenue:2.5
~Sales_data:000001936AE052B0
~Base_data:000001936AE052F8
~Sales_data:000001936AE03830
~Base_data:000001936AE03878
~Sales_data:000000547A36EF50
~Base_data:000000547A36EF98

參考
error LNK1169: 找到一個或多個多重定義的符號”的解決方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章