纯虚函数和抽象类以及实例

抽象类

当把一个类作为一个类型时,都假设程序将创建这种类型的对象,但是,在某些情况下,定义一个程序员永远不打算实例化任何对象的类也是非常有用的,这样的类称为抽象类,因为通常抽象类在类的继承层次结构中作为基类,所以我们有时也称这种类为抽象基类。这种类不能用来实例化对象,抽象类其实是不完整的,其派生类必须在这些类的对象实例化之前定义那些缺少的部分,比如实例化对象。

构造抽象类的目的是为其他类提供适合的基类。可以用来实例化对象的类称为具体类。这些类为它们声明的每一个成员函数定义或继承实现。例如可以定义一个抽象基类TwoDimensionalShape(二维形状),然后由它派生出具体类,如Square(正方形)、Circle(圆)和Triangle(三角形)。或者定义一个抽象基类ThreeDimensionalShape(),并由它派生出具体类,如Cube(立方体)、Sphere(球)和Cylinder(圆柱体)。抽象基类太宽泛以至于无法定义真实的对象,在可以考虑实例化对象之前,需要更加具体的内容。例如,有人说“请绘制这个二维形状”,那么到底该绘制什么形状呢?具体类提供了详细的说明,使得类实例化对象是合理的。

纯虚函数

通过声明类的一个或多个virtual函数为纯virtual函数,可以使一个类成为抽象类。一个纯virtual函数是在声明时“初始化值为0”的函数,如下所示:

virtual void fun() = 0;

“=0”称为纯指示符。纯virtual函数不提供函数的具体实现,每个派生的具体类必须重写所有基类的纯virtual函数的定义,提供这些函数的具体实现。virtual函数和纯virtual函数之间的区别是:virtual函数有函数的实现,并且提供派生类是否重写这些函数的选择权。相反,纯virtual函数并不提供函数的实现,需要派生类重写这些函数以使派生类成为具体类,否则派生类仍然是抽象类。

实例演示(应用多态性的工资发放系统)

问题描述:某家公司按周支付雇员工资。雇员一共有3类:定薪雇员,不管每周工作多长时间都领取固定的周薪;佣金雇员,工资完全是销售业绩提成;带薪佣金雇员,工资是基本工资加销售业绩提成。在这次工资发放阶段,公司决定奖励带薪佣金雇员,把他们的基本工资提高10%。公司想实现一个C++程序多态地执行工资的计算。

我们使用抽象类Employee表示通常概念的雇员类。直接从Employee类中派生的是类SalariedEmployee(定薪雇员类)、CommissionEmployee(佣金雇员类)。而类BasePlueCommissionEmployee(带薪佣金雇员类)又是从CommissionEmployee类直接派生的。抽象基类Employee声明了类层次结构的接口,即程序可以对所有的Employee类对象调用的一组成员函数集合。每个雇员,不论他的工资计算方式如何,都有姓名及社会保险号码,因此在抽象基类Employee中含有 private数据成员First- Name,LastName和SocialSecurityNumber。

抽象基类Employee:Employee类除了包含操作Employee类的数据成员的各种Get和Set函数之外,还提供成员函数earnings()和print()。earnings函数应用于所有的雇员,但是每项收入的计算取决于官员的类型。所以在基类Employee中把earnings()函数设置为纯虚函数,因为这个函数默认地一种实现是没有任何意义的,没有足够的信息决定应该返回的收入是多少。每个派生类都用合适的实现来重写earnings()函数。要计算一个雇员的收入,程序把一个雇员对象的地址赋给一个基类Employee指针,然后调用该对象的earnings函数。我们维护一个Employee指针的vector对象,每个指针都指向一个Employee对象。程序迭代访问此vector对象并调用每个Employee对象的earnings()函数。C++多态得执行这些函数调用,因为Employee类中含有纯虚函数,迫使所有希望成为具体类的那些直接继承Employee类的派生类都重写了earnings()函数。Employee类中的print()函数显示雇员的姓名和社会保险号码。每个Employee类的派生类都重写了print()函数,输出雇员的类型之后紧接着还输出了雇员的其他信息。函数print()可以调用earnings()函数,即使print()函数是类的一个虚函数。

employee.h

#ifndef EMPLOYEE_H
#define EMPLOYEE_H
#include <string>
#include <iostream>
using namespace std;
class Employee
{
public:
    Employee(string, string, string);		//构造函数
    virtual     ~Employee(){}				//虚析构函数
    void SetFirstName(string &);			//设置名
    string GetFirstName();
    void SetLastName(string &);				//设置姓
    string GetLastName();
    void SetSocialSecurityNumber(string &);	        //设置社会保险号码
    string GetSocialSecurityNumber();
    virtual double earnings() = 0;			//纯虚函数
    virtual void print();
private:
    string FirstName;
    string LastName;
    string SocialSecurityNumber;
};
#endif 

employee.cpp

#include "employee.h"
#include <iostream>
using namespace std;
//构造函数
Employee::Employee(string first, string last, string ssn)
	:FirstName(first), LastName(last), SocialSecurityNumber(ssn)
{
}
//设置名
void Employee::SetFirstName(string &first)
{
    FirstName = first;
}
//获取名
string Employee::GetFirstName()
{
    return FirstName;
}
//设置姓
void Employee::SetLastName(string &last)
{
    LastName = last;
}
//获取姓
string Employee::GetLastName()
{
    return LastName;
}
//设置社会保险号码
void Employee::SetSocialSecurityNumber(string &ssn)
{
    SocialSecurityNumber = ssn;
}
//获取社会保险号码
string Employee::GetSocialSecurityNumber()
{
    return SocialSecurityNumber;
}
//打印
void Employee::print()
{
    cout << GetFirstName() << " " << GetLastName() <<
		"'s Social Security Number is : " << GetSocialSecurityNumber() << endl;
}

SalaredEmployee(定薪雇员)类是从Employee类派生而来的,其公有成员函数包括:一个以名、姓、社会保险号码和周薪为参数的构造函数,一个virtual析构函数,一个给数据成员WeeklySalary赋值的SetWeeklySalary()函数,一个返回周薪的GetWeeklySalary()函数,还有一个计算定薪雇员收入的virtual函数earnings()函数,打印雇员信息的print()函数,输出内容是雇员类型以及由基类Employee的print()函数和SalariedEmployee类的GetWeeklySalary()函数产生的雇员的特定信息。SalariedEmployee类中的print()函数重写了基类Employee的print()函数,如果SalariedEmployee类不重写print()函数,那么SalariedEmployee类将继承Employee类中的print()函数,这样的话,SalariedEmployee类的print()函数只是简单的返回雇员的姓名和社会保险号码,而这些信息并不能充分的描述一个SalariedEmployee雇员。为了打印SalariedEmployee官员的完整信息,派生类的print()函数首先输出“Salaried Employee :”,然后通过使用作用域分辨运算符调用基类的print()函数,从而输出基类Employee的基本信息。

 SalariedEmployee.h

#include <string>
#include "employee.h"
#include <iostream>
using namespace std;
class SalariedEmployee:public Employee
{
public:
    SalariedEmployee(string, string, string, double = 0.0);	//构造函数
    virtual ~SalariedEmployee()					        //虚析构函数
    {}
    void SetWeeklySalary(double);					//设置周薪
    double GetWeeklySalary();					        //获取周薪
    virtual double earnings() override;				        //计算收入
    virtual void print() override;
private:
    double WeeklySalary;
};

SalariedEmployee.cpp

#include <iostream>
#include <stdexcept>
#include "SalariedEmployee.h"
using namespace std;

//构造函数
SalariedEmployee::SalariedEmployee(string first, string last, string ssn, double salary)
	:Employee(first, last, ssn)
{
    SetWeeklySalary(salary);
}
//设置周薪
void SalariedEmployee::SetWeeklySalary(double salary)
{
    if (salary > 0.0)
    {
	WeeklySalary = salary;
    }
    else
    {
	throw invalid_argument("Weekly Salary must be > 0.0");
    }
}
//获取周薪
double SalariedEmployee::GetWeeklySalary()
{
    return WeeklySalary;
}
//计算收入
double SalariedEmployee::earnings()
{
    return GetWeeklySalary();
}
//打印
void SalariedEmployee::print()
{
    cout << "Salaried Employee : ";
    Employee::print();
    cout << "Weekly Salary is : " << GetWeeklySalary() << endl;
}

CommissionEmployee(佣金雇员)类同样也是继承自Employee类。其公有成员函数包括一个以姓名、社会保险号码、销售总额、提成比例为参数的构造函数;给数据成员GrossSales(销售总额)和CommissionRate(提成比例)分别赋值的SetGrossSales()和SetCommissionRate()函数,以及获取销售总额和提成比例的函数,计算一个佣金雇员收入的earnings()函数,输出雇员类型以及详细信息的print()函数。

CommissionEmployee.h

#include <string>
#include "employee.h"
class CommissionEmployee :public Employee
{
public:
    CommissionEmployee(string, string, string, double = 0.0, double = 0.0);//构造函数
    virtual ~CommissionEmployee()		//虚析构函数
    {}
    void SetCommissionRate(double);		//设置提成比例
    double GetCommissionRate();			//获取提成比例
    void SetGrossSales(double);			//设置销售总额
    double GetGrossSales();			//获取销售总额
    virtual double earnings() override;		//计算收入
    virtual void print() override;		//打印
private:
    double CommissionRate;
    double GrossSales;
};

CommissionEmployee.cpp

#include <iostream>
#include "CommissionEmployee.h"
#include <stdexcept>
using namespace std;
//构造函数
CommissionEmployee::CommissionEmployee(string first, string last, string ssn, double sales, double rate)
	:Employee(first, last, ssn)
{
    SetCommissionRate(rate);
    SetGrossSales(sales);
}
//设置提成比例
void CommissionEmployee::SetCommissionRate(double rate)
{
    if (rate > 0.0 && rate < 1.0)
    {
	CommissionRate = rate;
    }
    else
    {
	throw invalid_argument("Commission Rate must be > 0.0 and < 1.0");
    }
}
//设置销售总额
void CommissionEmployee::SetGrossSales(double sales)
{
    if (sales >= 0.0)
    {
	GrossSales = sales;
    }
    else
    {
	throw invalid_argument("Gross Sales must be >= 0.0");
    }
}
//获取提成比例
double CommissionEmployee::GetCommissionRate()
{
    return CommissionRate;
}
//获取销售总额
double CommissionEmployee::GetGrossSales()
{
    return GrossSales;
}
//计算收入
double CommissionEmployee::earnings()
{
    return GetGrossSales() * GetCommissionRate();
}
//打印
void CommissionEmployee::print()
{
    cout << "Commission Employee : ";
    Employee::print();
    cout << "gross sales:" << GetGrossSales() << ",commission rate:" << GetCommissionRate() << endl;
}

还有一种雇员类型叫带薪佣金雇员。BasePlusCommissionEmployee(带薪佣金雇员)类继承自CommissionEmployee类,是Employee类的间接派生类。其成员函数包括构造函数,设置基础工资的SetBaseSalary()函数和一个返回基础工资的GetBaseSalary()函数,以及计算收入的earnings()函数和打印print()函数。

BasePlusCommissionEmployee.h

#include <string>
#include <iostream>
#include "CommissionEmployee.h"
class BasePlusCommissionEmployee :CommissionEmployee
{
public:
    BasePlusCommissionEmployee(string, string, string, double = 0.0, double = 0.0, double = 0.0);		//构造函数
    virtual ~BasePlusCommissionEmployee()	//虚析构函数
    {}
    void SetBaseSalary(double);		        //设置基础工资
    double GetBaseSalary();			//获取基础工资
    virtual double earnings() override;		//计算收入
    virtual void print();			//打印
private:
    double BaseSalary;
};

BasePlusCommissionEmployee.cpp

#include "BasePlusCommissionEmployee.h"
#include <iostream>
#include <stdexcept>
using namespace std;
//构造函数
BasePlusCommissionEmployee::BasePlusCommissionEmployee(string first, string last, string ssn, double sales, double rate, double basesalary)
	:CommissionEmployee(first, last, ssn, sales, rate)
{
    SetBaseSalary(basesalary);
}
//设置基础工资
void BasePlusCommissionEmployee::SetBaseSalary(double basesalary)
{
    if (basesalary >= 0.0)
    {
	BaseSalary = basesalary;
    }
    else
    {
	throw invalid_argument("Base Salary must be >= 0.0");
    }
}
//获取基础工资
double BasePlusCommissionEmployee::GetBaseSalary()
{
    return BaseSalary;
}
double BasePlusCommissionEmployee::earnings()
{
    return GetBaseSalary() + CommissionEmployee::earnings();
}
//打印
void BasePlusCommissionEmployee::print()
{
    cout << "Base-Salary :";
    CommissionEmployee::print();
    cout << ",base salsry:" << GetBaseSalary() << endl;
}

主函数:

#include <vector>
#include <iomanip>
#include <iostream>
#include "BasePlusCommissionEmployee.h"
#include "CommissionEmployee.h"
#include "employee.h"
#include "SalariedEmployee.h"
using namespace std;
void virtualViaPointer(Employee *baseClassPtr)
{
    baseClassPtr->print();
    cout << "total salary:" << baseClassPtr->earnings() << endl << endl;
}
void virtualViaRefrence(Employee &baseClassRef)
{
    baseClassRef.print();
    cout << "total salary:" << baseClassRef.earnings() << endl << endl;
}
int main()
{
    cout << fixed << setprecision(2);
    //创建定薪雇员类对象(定薪800)
    SalariedEmployee salariedemployee("John", "Smith", "1-11-111", 800);
    //创建佣金雇员类对象(销售总额10000,提成比例6%)
    CommissionEmployee commissionemployee("Sue", "Jones", "2-22-222", 10000, 0.06);
    //创建带薪佣金雇员类对象(销售总额5000,提成比例4%,基础工资300)
    BasePlusCommissionEmployee basepluscommissionemployee("Bob", "Lewis", "3-33-333", 5000, 0.04, 300);
    //输出各个员工的信息
    salariedemployee.print();
    cout << "total salary:" << salariedemployee.earnings() << endl << endl;
    commissionemployee.print();
    cout << "total salary:" << commissionemployee.earnings() << endl << endl;
    basepluscommissionemployee.print();
    cout << "total salary:" << basepluscommissionemployee.earnings() << endl << endl;

    //创建vector对象,包含3个Employee类指针
    vector<Employee *> employees(3);
    employees[0] = &salariedemployee;
    employees[1] = &commissionemployee;
    employees[2] = &basepluscommissionemployee;
    //循环遍历vector对象,并为每个元素调用virtualViaPointer()函数
    for (Employee *employeePtr : employees)
    {
	virtualViaPointer(employeePtr);
    }
    //循环遍历vector对象,并为每个元素调用virtualViaRefrence()函数
    {
	for (Employee *employeePtr : employees)
	{
	    virtualViaRefrence(*employeePtr);
	}
    }
    return 0;
}

virtualViaPointer()函数用参数baseClassPtr接收保存在employees元素中的地址。每次对virtualViaPointer()函数的调用都利用baseClassPtr调用print()函数和earnings()函数。该函数仅仅知道基类类型是Employee,因此在编译时编译器并不知道通过baseClassPtr在那时指向的对象的函数。输出表明对于每个类程序的确调用了适当的函数,并输出了每个对象的正确信息。

virtualViaRefrence()函数用参数baseClassRefer(类型是引用)接收间接引用保存在employees元素中的指针后形成的引用。每次对virtualViaPointer()函数的调用都会通过引用baseClassRef调用print()函数和earnings()函数,以表明利用基类引用同样产生了多态性的行为。每次虚函数的调用都会调用baseClassRef在执行时所引用对象的函数。

执行结果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值