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: 找到一个或多个多重定义的符号”的解决方法

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