文章目录
- 0. 前言
- 11.1 简介
- 11.2 基类和派生类
- 11.3 基类和派生类之间的关系
- 11.4 派生类中的构造函数和析构函数
- 11.5 public、protected和private继承
- 练习题
- 结语
0. 前言
《C++大学教程》 第11章 笔记更一下。
附部分课后题代码。
11.1 简介
继承是软件复用的一种方式,通过继承,可以吸收现有类的各种性能(即数据和行为)的基础上,再加以定制或增强来创建新类。
现有的类称为“基类”,继承实现的新类成为“派生类”。
C++提供了三种继承:public
、protected
和private
继承。
在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
!
个人水平有限,有问题欢迎各位大神批评指正!