此實例演示能帶你貫穿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