此实例演示能带你贯穿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