《C++大学教程》 第11章 面对对象编程:继承 笔记

0. 前言

《C++大学教程》 第11章 笔记更一下。
附部分课后题代码。

11.1 简介

继承是软件复用的一种方式,通过继承,可以吸收现有类的各种性能(即数据和行为)的基础上,再加以定制或增强来创建新类。

现有的类称为“基类”,继承实现的新类成为“派生类”。

C++提供了三种继承:publicprotectedprivate继承。
public继承中,每个派生类的对象同时也是基类的对象。但是,基类的对象不是派生类的对象。即派生类为基类的特殊化。

“是一个(is-a)”关系表示继承。
“有一个(has-a)”关系表示组成。对象可以把其他类的一个或多个对象拿来作为自己的成员。

11.2 基类和派生类

在各种形式的继承中,基类的private成员都不能被它的派生类直接访问,但这些private基类成员仍得到了继承(即它们仍被视为是派生类的一部分)。在public继承中,基类的所有其他成员在成为派生类的成员时仍保持了其原始的成员访问权限。通过基类继承而来的成员函数,派生类能够操作基类中的private成员。

友元函数是不被继承的。

11.3 基类和派生类之间的关系

11.3.1 创建并使用类CommissionEmployee

// Fig. 11.4: CommissionEmployee.h
// CommissionEmployee class definition represents a commission employee.
#ifndef COMMISSION_H
#define COMMISSION_H

#include <string> // C++ standard string class

class CommissionEmployee
{
public:
   CommissionEmployee( const std::string &, const std::string &, 
      const std::string &, double = 0.0, double = 0.0 );
   
   void setFirstName( const std::string & ); // set first name
   std::string getFirstName() const; // return first name

   void setLastName( const std::string & ); // set last name
   std::string getLastName() const; // return last name

   void setSocialSecurityNumber( const std::string & ); // set SSN
   std::string getSocialSecurityNumber() const; // return SSN

   void setGrossSales( double ); // set gross sales amount
   double getGrossSales() const; // return gross sales amount

   void setCommissionRate( double ); // set commission rate (percentage)
   double getCommissionRate() const; // return commission rate

   double earnings() const; // calculate earnings
   void print() const; // print CommissionEmployee object
private:
   std::string firstName;
   std::string lastName;
   std::string socialSecurityNumber;
   double grossSales; // gross weekly sales
   double commissionRate; // commission percentage
}; // end class CommissionEmployee

#endif

// Fig. 11.5: CommissionEmployee.cpp
// Class CommissionEmployee member-function definitions.
#include <iostream>
#include <stdexcept>
#include "CommissionEmployee.h" // CommissionEmployee class definition
using namespace std;

// constructor
CommissionEmployee::CommissionEmployee( 
   const string &first, const string &last, const string &ssn, 
   double sales, double rate )
{
   firstName = first; // should validate
   lastName = last; // should validate
   socialSecurityNumber = ssn; // should validate
   setGrossSales( sales ); // validate and store gross sales
   setCommissionRate( rate ); // validate and store commission rate
} // end CommissionEmployee constructor

// set first name
void CommissionEmployee::setFirstName( const string &first )
{
   firstName = first; // should validate
} // end function setFirstName

// return first name
string CommissionEmployee::getFirstName() const
{
   return firstName;
} // end function getFirstName

// set last name
void CommissionEmployee::setLastName( const string &last )
{
   lastName = last; // should validate
} // end function setLastName

// return last name
string CommissionEmployee::getLastName() const
{
   return lastName;
} // end function getLastName

// set social security number
void CommissionEmployee::setSocialSecurityNumber( const string &ssn )
{
   socialSecurityNumber = ssn; // should validate
} // end function setSocialSecurityNumber

// return social security number
string CommissionEmployee::getSocialSecurityNumber() const
{
   return socialSecurityNumber;
} // end function getSocialSecurityNumber

// set gross sales amount
void CommissionEmployee::setGrossSales( double sales )
{
   if ( sales >= 0.0 )
      grossSales = sales;
   else
      throw invalid_argument( "Gross sales must be >= 0.0" );
} // end function setGrossSales

// return gross sales amount
double CommissionEmployee::getGrossSales() const
{
   return grossSales;
} // end function getGrossSales

// set commission rate
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" );
} // end function setCommissionRate

// return commission rate
double CommissionEmployee::getCommissionRate() const
{
   return commissionRate;
} // end function getCommissionRate

// calculate earnings
double CommissionEmployee::earnings() const
{
   return commissionRate * grossSales;
} // end function earnings

// print CommissionEmployee object
void CommissionEmployee::print() const
{
   cout << "commission employee: " << firstName << ' ' << lastName 
      << "\nsocial security number: " << socialSecurityNumber 
      << "\ngross sales: " << grossSales 
      << "\ncommission rate: " << commissionRate;
} // end function print

// Fig. 11.6: fig11_06.cpp
// CommissionEmployee class test program.
#include <iostream>
#include <iomanip>
#include "CommissionEmployee.h" // CommissionEmployee class definition
using namespace std;

int main()
{
   // instantiate a CommissionEmployee object
   CommissionEmployee employee(
      "Sue", "Jones", "222-22-2222", 10000, .06 );
   
   // set floating-point output formatting
   cout << fixed << setprecision( 2 );

   // get commission employee data
   cout << "Employee information obtained by get functions: \n" 
      << "\nFirst name is " << employee.getFirstName() 
      << "\nLast name is " << employee.getLastName() 
      << "\nSocial security number is " 
      << employee.getSocialSecurityNumber() 
      << "\nGross sales is " << employee.getGrossSales() 
      << "\nCommission rate is " << employee.getCommissionRate() << endl;

   employee.setGrossSales( 8000 ); // set gross sales
   employee.setCommissionRate( .1 ); // set commission rate

   cout << "\nUpdated employee information output by print function: \n" 
      << endl;
   employee.print(); // display the new employee information

   // display the employee's earnings
   cout << "\n\nEmployee's earnings: $" << employee.earnings() << endl;
} // end main

11.3.3 创建CommissionEmployee-BasePlusCommissionEmployee继承层次结构

类定义中的冒号:表示这是一个继承,关键词public指明该继承的类型。
作为由public继承形成的派生类,类BasePlusCommissionEmployee继承了类CommissionEmployee中除构造函数的所有成员。
每个类都提供特定于自己的构造函数。请注意,析构函数同样不能继承。

// Fig. 11.10: BasePlusCommissionEmployee.h
// BasePlusCommissionEmployee class derived from class 
// CommissionEmployee.
#ifndef BASEPLUS_H
#define BASEPLUS_H

#include <string> // C++ standard string class
#include "CommissionEmployee.h" // CommissionEmployee class declaration

class BasePlusCommissionEmployee : public CommissionEmployee
{
public:
   BasePlusCommissionEmployee( const std::string &, const std::string &, 
      const std::string &, double = 0.0, double = 0.0, double = 0.0 );
   
   void setBaseSalary( double ); // set base salary
   double getBaseSalary() const; // return base salary

   double earnings() const; // calculate earnings
   void print() const; // print BasePlusCommissionEmployee object
private:
   double baseSalary; // base salary
}; // end class BasePlusCommissionEmployee

#endif

构造函数引入了基类初始化器语法,通过成员初始化器将参数传递给基类的构造函数。

编译器对任何没有显式声明地包含构造函数的类,将提供一个无参数的默认构造函数。

```cpp
// Fig. 11.11: BasePlusCommissionEmployee.cpp
// Class BasePlusCommissionEmployee member-function definitions.
#include <iostream>
#include <stdexcept>
#include "BasePlusCommissionEmployee.h" // class definition
using namespace std;

// constructor
BasePlusCommissionEmployee::BasePlusCommissionEmployee( 
   const string &first, const string &last, const string &ssn, 
   double sales, double rate, double salary ) 
   // explicitly call base-class constructor
   : CommissionEmployee( first, last, ssn, sales, rate )
{
   setBaseSalary( salary ); // validate and store base salary
} // end BasePlusCommissionEmployee constructor

// set base salary
void BasePlusCommissionEmployee::setBaseSalary( double salary )
{
   if ( salary >= 0.0 )                                        
      baseSalary = salary;                                     
   else                                                        
      throw invalid_argument( "Salary must be >= 0.0" );       
} // end function setBaseSalary

// return base salary
double BasePlusCommissionEmployee::getBaseSalary() const
{
   return baseSalary;
} // end function getBaseSalary

// calculate earnings
double BasePlusCommissionEmployee::earnings() const
{
   // derived class cannot access the base class's private data
   return baseSalary + ( commissionRate * grossSales );
} // end function earnings

// print BasePlusCommissionEmployee object
void BasePlusCommissionEmployee::print() const
{
   // derived class cannot access the base class's private data
   cout << "base-salaried commission employee: " << firstName << ' ' 
      << lastName << "\nsocial security number: " << socialSecurityNumber 
      << "\ngross sales: " << grossSales 
      << "\ncommission rate: " << commissionRate 
      << "\nbase salary: " << baseSalary;
} // end function print

访问基类的private成员而产生的编译错误

C++严格限制对private数据成员的访问。
派生类不能访问基类的private数据。

防止BasePlusCommissionEmployee中的错误

BasePlusCommissionEmployee中的错误可以以通过使用从类CommissionEmployee中继承的获取成员函数来避免。

在派生类的头文件中使用#include包含基类的头文件

在派生类的头文件中,我们使用#include来包含基类的头文件。

原因有三:

  • 为了在派生类中使用基类的类名,必须告诉编译器这个基类是存在的。
  • 编译器需要使用类定义来决定类对象的大小。
  • 编译器可以据此判断派生类是否正确地使用了由基类继承而来的成员。

11.3.4 使用protected数据的CommissionEmployee-BasePlusCommissionEmployee继承层次结构

基类的protected成员既可以在基类的体内被基类的成员和友元访问,又可以被由基类派生的任何类的成员和友元访问。

使用protected数据定义基类CommissionEmployee

CommissionEmployee成员函数函数的实现同上,不再给出。

// Fig. 11.12: CommissionEmployee.h
// CommissionEmployee class definition with protected data.
#ifndef COMMISSION_H
#define COMMISSION_H

#include <string> // C++ standard string class

class CommissionEmployee
{
public:
   CommissionEmployee( const std::string &, const std::string &, 
      const std::string &, double = 0.0, double = 0.0 );
   
   void setFirstName( const std::string & ); // set first name
   std::string getFirstName() const; // return first name

   void setLastName( const std::string & ); // set last name
   std::string getLastName() const; // return last name

   void setSocialSecurityNumber( const std::string & ); // set SSN
   std::string getSocialSecurityNumber() const; // return SSN

   void setGrossSales( double ); // set gross sales amount
   double getGrossSales() const; // return gross sales amount

   void setCommissionRate( double ); // set commission rate (percentage)
   double getCommissionRate() const; // return commission rate

   double earnings() const; // calculate earnings
   void print() const; // print CommissionEmployee object
protected:
   std::string firstName;
   std::string lastName;
   std::string socialSecurityNumber;
   double grossSales; // gross weekly sales
   double commissionRate; // commission percentage
}; // end class CommissionEmployee

#endif

类BasePlusCommissionEmployee

BasePlusCommissionEmployee的定义同上,这里不再给出。

BasePlusCommissionEmployee的构造函数必须显式调用类的CommissionEmployee的构造函数,因为类CommissionEmployee没有包含可以被隐式调用的默认构造函数。

使用protected数据的注意事项

在多数情况下,使用private数据成员是更好的软件工程方法,把代码优化交给编译器去做就可以了。

使用protected数据将产生的两个问题:

  • 派生类对象不必使用成员函数设置基类的protected数据成员的值,很容易将无效的值赋给基类的protected数据,导致对象处于不一致的状态。
  • 派生类成员函数实现很可能太依赖基类的实现。

在基类仅向其派生类和友元提供服务(也就是非private的成员函数)时,使用protected成员访问说明符是合适的。

将基类的数据成员声明为private(而不是protected),使程序员可以在不修改派生类实现的同时修改基类的实现。

11.3.5 使用private数据的CommissionEmployee-BasePlusCommissionEmployee继承层次结构

// Fig. 11.14: CommissionEmployee.cpp
// Class CommissionEmployee member-function definitions.
#include <iostream>
#include <stdexcept>
#include "CommissionEmployee.h" // CommissionEmployee class definition
using namespace std;

// constructor
CommissionEmployee::CommissionEmployee( 
   const string &first, const string &last, const string &ssn, 
   double sales, double rate )
   : firstName( first ), lastName( last ), socialSecurityNumber( ssn )
{
   setGrossSales( sales ); // validate and store gross sales
   setCommissionRate( rate ); // validate and store commission rate
} // end CommissionEmployee constructor

// set first name
void CommissionEmployee::setFirstName( const string &first )
{
   firstName = first; // should validate
} // end function setFirstName

// return first name
string CommissionEmployee::getFirstName() const
{
   return firstName;
} // end function getFirstName

// set last name
void CommissionEmployee::setLastName( const string &last )
{
   lastName = last; // should validate
} // end function setLastName

// return last name
string CommissionEmployee::getLastName() const
{
   return lastName;
} // end function getLastName

// set social security number
void CommissionEmployee::setSocialSecurityNumber( const string &ssn )
{
   socialSecurityNumber = ssn; // should validate
} // end function setSocialSecurityNumber

// return social security number
string CommissionEmployee::getSocialSecurityNumber() const
{
   return socialSecurityNumber;
} // end function getSocialSecurityNumber

// set gross sales amount
void CommissionEmployee::setGrossSales( double sales )
{
   if ( sales >= 0.0 )
      grossSales = sales;
   else
      throw invalid_argument( "Gross sales must be >= 0.0" );
} // end function setGrossSales

// return gross sales amount
double CommissionEmployee::getGrossSales() const
{
   return grossSales;
} // end function getGrossSales

// set commission rate
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" );
} // end function setCommissionRate

// return commission rate
double CommissionEmployee::getCommissionRate() const
{
   return commissionRate;
} // end function getCommissionRate

// calculate earnings
double CommissionEmployee::earnings() const
{
   return getCommissionRate() * getGrossSales();
} // end function earnings

// print CommissionEmployee object
void CommissionEmployee::print() const
{
   cout << "commission employee: " 
      << getFirstName() << ' ' << getLastName() 
      << "\nsocial security number: " << getSocialSecurityNumber() 
      << "\ngross sales: " << getGrossSales() 
      << "\ncommission rate: " << getCommissionRate();
} // end function print

// Fig. 11.15: BasePlusCommissionEmployee.cpp
// Class BasePlusCommissionEmployee member-function definitions.
#include <iostream>
#include <stdexcept>
#include "BasePlusCommissionEmployee.h"
using namespace std;

// constructor
BasePlusCommissionEmployee::BasePlusCommissionEmployee( 
   const string &first, const string &last, const string &ssn, 
   double sales, double rate, double salary ) 
   // explicitly call base-class constructor
   : CommissionEmployee( first, last, ssn, sales, rate )
{
   setBaseSalary( salary ); // validate and store base salary
} // end BasePlusCommissionEmployee constructor

// set base salary
void BasePlusCommissionEmployee::setBaseSalary( double salary )
{
   if ( salary >= 0.0 )                                        
      baseSalary = salary;                                     
   else                                                        
      throw invalid_argument( "Salary must be >= 0.0" );       
} // end function setBaseSalary

// return base salary
double BasePlusCommissionEmployee::getBaseSalary() const
{
   return baseSalary;
} // end function getBaseSalary

// calculate earnings
double BasePlusCommissionEmployee::earnings() const
{
   return getBaseSalary() + CommissionEmployee::earnings();
} // end function earnings

// print BasePlusCommissionEmployee object
void BasePlusCommissionEmployee::print() const
{
   cout << "base-salaried ";

   // invoke CommissionEmployee's print function
   CommissionEmployee::print();                 
   
   cout << "\nbase salary: " << getBaseSalary();
} // end function print

如果决定更改数据成员的名称,只需修改直接操作数据成员的获取和设置成员函数即可。

派生类调用基类的函数的语法格式:基类成员函数名之前加基类名和二元作用域分辨运算符(::)。

11.4 派生类中的构造函数和析构函数

在实例化派生类的对象时,基类的构造函数会被立即调用来初始化派生类对象中的基类数据成员,然后派生类的构造函数会初始化其他派生类的数据成员。

当销毁派生类的对象时,先调用派生类的构造函数,然后调用基类的析构函数。

派生类不会继承基类的构造函数、析构函数和重载的赋值运算符。但是,派生类的构造函数、析构函数和重载的赋值运算符可以调用基类的构造函数、析构函数和重载的赋值运算符函数。

11.5 public、protected和private继承

当采用public继承派生一个类时,基类的public成员成为派生类中的public成员,基类的protected成员成为派生类中的protected成员。

当采用protected继承派生一个类时,基类的public成员和protected成员都变成派生类中的protected成员。

当采用private继承派生一个类时,基类的public成员和protected成员都变成派生类中的private成员。

练习题

11.10 账户的继承层次结构

基类:账户类Account

#ifndef ACCOUNT_H
#define ACCOUNT_H

class Account
{
public:
	Account(double = 0.0);
	void credit(double);
	void debit(double);
	double getBalance() const;
	void setBalance(double);
private:
	double balance;
};

#endif 
#include "Account.h"

#include <iostream>
using namespace std;

Account::Account(double num)
{
	setBalance(num);
}

void Account::credit(double num)
{
	balance += num;
}

void Account::debit(double num) 
{
	if (balance >= num)
	{
		balance -= num;
	}
	else
		cout << "Debit amount exceeded account balance." << endl; 
}

double Account::getBalance() const
{
	return balance;
}
void Account::setBalance(double num)
{
	if (num >= 0)
		balance = num;
	else
	{
		balance = 0;
		cout << "Initialization balance is an invalid value!" << endl;
	}
}

派生类:存款账户类SavingAccount

#ifndef SAVINGACCOUNT_H
#define SAVINGACCOUNT_H

#include "Account.h"

class SavingAccount : public Account
{
public:
	SavingAccount(double = 0.0, double = 0.0);
	double calculateInterest() const;
	void setInterest();
private:
	double rate;
};

#endif
#include "SavingAccount.h"

#include <iostream>
using namespace std;

SavingAccount::SavingAccount(double numbalance, double ratenum)
	:Account(numbalance)
{ 
	rate = ratenum; 
}
double SavingAccount::calculateInterest() const 
{ 
	return (rate * Account::getBalance()); 
}
void SavingAccount::setInterest() 
{ 
	Account::setBalance(Account::getBalance()+ calculateInterest());
}

派生类:支票账户类CheckingAccount

#ifndef CHECKINGACCOUNT_H
#define CHECKINGACCOUNT_H

#include "Account.h"

class CheckingAccount : public Account
{
public:
	CheckingAccount(double = 0.0, double = 0.0);
	void credit(double);
	void debit(double);
private:
	double fee;
};
#endif

#include "CheckingAccount.h"

#include <iostream>
using namespace std;

CheckingAccount::CheckingAccount(double numbalance, double feenum)
	:Account(numbalance)
{
	fee = feenum;
}
void CheckingAccount::credit(double num)
{
	Account::setBalance(Account::getBalance() +  num - fee);
}
void CheckingAccount::debit(double num)
{
	
	if (Account::getBalance() >= num)
	{
		Account::setBalance(Account::getBalance() - num - fee);
	}
	else
		cout << "Debit amount exceeded account balance." << endl; ;
}

测试代码

#include"Account.h"
#include"SavingAccount.h"
#include"CheckingAccount.h"

#include<iostream>
using namespace std;

int main()
{
	cout << "Account:" << endl;
	Account testError(-100);
	cout << testError.getBalance() << endl;

	cout << endl;
	cout << "Account:" << endl;
	Account test(100);
	cout << test.getBalance() << endl;
	test.credit(100);
	cout << test.getBalance() << endl;
	test.debit(50);
	cout << test.getBalance() << endl;
	test.debit(250);
	cout << test.getBalance() << endl;

	cout << endl;
	cout << "SavingAccount:"  << endl;
	SavingAccount testSaving(100, 0.05);
	cout << "Interest:" << testSaving.calculateInterest() << endl;
	testSaving.setInterest();
	cout << testSaving.getBalance() << endl;
	testSaving.credit(100);
	cout << testSaving.getBalance() << endl;
	testSaving.debit(50);
	cout << testSaving.getBalance() << endl;
	testSaving.debit(250);
	cout << testSaving.getBalance() << endl;

	cout << endl;
	cout << "CheckingAccount:" << endl;
	CheckingAccount testChecking(100,5);
	cout << testChecking.getBalance() << endl;
	testChecking.credit(100);
	cout << testChecking.getBalance() << endl;
	testChecking.debit(50);
	cout << testChecking.getBalance() << endl;
	testChecking.debit(250);
	cout << testChecking.getBalance() << endl;
	
}

代码效果

在这里插入图片描述

结语

第11章已全部更完。

继承这一章相对比较简单。
感谢苏神帮我解决error C2248

个人水平有限,有问题欢迎各位大神批评指正!

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值