继承--is-a关系
2016-07-05 21:10
302 查看
C++有3种继承方式:公有继承、保护继承、私有继承。公有继承是最常用的方式,它建立一种is-a关系,即派生类对象也是一个基类对象,可以对基类对象执行的任何操作,也可以对派生类对象执行。
如果希望同一个方法在派生类和基类中的行为是不同的,也就是说,方法的行为应取决于调用该方法的对象。这种较复杂的行为成为多态--具有多种形态,就是指同一个方法的想行为随上下文而异。有两种机制可用于实现多态公有继承:
1.在派生类中重新定义基类的方法。
2.使用虚方法。
为了说明上述性质,我们来看另一个例子:一个类用于 表示基本支票账户-Brass,另一个类用于表示代表Brass Plus支票账户,它添加了透支保护特性。也就是说,如果用户签出一张超出其存款余额的支票--但是超出的数额不是很大,银行将支付这张支票,对超出的部分收取额外的费用,并追加罚款。
下面是用于Brass支票账户的信息:
1.客户姓名
2.账户
3.当前余额
下面是可执行的操作:
1.创建账户
2.存款
3.取款
4.显示账户信息
而Brass Plus包含Brass的所有信息以及以下信息项:
1.透支上限
2.透支贷款利率
3.当前透支总额
不需要新增操作,担有两种操作的实现不同
1.对于取款操作,必须考虑透支保护
2.显示操作必须显示Brass Plus账户的其他信息
我们将第一个类命名为Brass,第二个类为BrassPlus。很明显,应从Prass公有派生出PrassPlus类。
程序清单 brass.h
//brass.h--bank account classes
#ifndef BRASS_H_
#define BRASS_H_
//Brass Account class
class Brass
{
private:
enum {MAX=35};
char fullName[MAX];
long acctNum;
double balance;
public:
Brass (const char *s = "Nullbody",long an = -1,
double bal = 0.0);
void Deposit(double amt);
virtual void withdraw(double amt);
double Balance()const;
virtual void ViewAcct()const;
virtual ~Brass(){}
};
//Brass plus Account Class
class BrassPlus: public Brass
{
private:
double maxloan;
double rate;
double owesBank;
public:
BrassPlus(const char *s = "Nullbody",long an=-1,
double bal=0.0,double ml=500,double r=0.10);
BrassPlus(const Brass & ba,double ml=500,double r=0.1);
virtual void ViewAcct()const;
virtual void Withdraw(double amt);
void ResetMax(double m){maxloan = m;}
void ReseRate(double r){rate = r;}
void ResetOwes(){owesBank = 0;}
};
#endif
对于上述程序清单,有以下说明:
BrassPlus类在Brass类的基础上添加了3个私有数据成员和3个公有成员函数。
Brass类和BrassPlus类都声明了ViewAcct()和Widthdraw()方法,但BrassPlus对象和Brass对象的这些方法的行为是不同的。
Brass类在声明ViewAcct()和Withdraw()时使用了新关键字virtual。这些方法被称为虚方法(virtual method)。
Brass还声明了一个虚拟析构函数,虽然该函数没进行任何操作。
记住:如果要在派生类中重新定义基类的方法,通常应将基类方法声明为虚拟的,这样,程序将根据对象类型而不是引用或指针的类型来选择方法版本;为基类声明一个虚拟析构函数也是一种惯例。
程序清单 brass.cpp
//brass.cpp--bank account class methods
#include <iostream>
#include <cstring>
#include "brass.h"
using std::cout;
using std::ios_base;
using std::endl;
//Brass methods
Brass::Brass(const char *s,long an,double bal)
{
std::strncpy(fullName,s,MAX - 1);
fullName[MAX - 1] - '\0';
acctNum = an;
balance = bal;
}
void Brass::Deposit(double amt)
{
if (amt < 0)
cout << "Neqative deposit not allowed: "
<< "deposit is cancelled.\n";
else
balance += amt;
}
void Brass::withdraw(double amt)
{
if (amt < 0)
cout << "Withdrawal amount must be positive: "
<< "withdrawall canceled.\n";
else if (amt <= balance)
balance -= amt;
else
cout << "Withdrawal amount of $" << amt
<< " exceeds your balance.\n"
<< "Withdrawal canceled.\n";
}
double Brass::Balance()const
{
return balance;
}
void Brass::ViewAcct()const
{
//set up ####.## format
ios_base::fmtflags initialState =
cout.setf(ios_base::fixed,ios_base::floatfield);
cout.setf(ios_base::showpoint);
cout.precision(2);
cout << "Client: " << fullName << endl;
cout << "Account Number: " << acctNum << endl;
cout << "Balance: $" << balance << endl;
cout.setf(initialState);//restore original format
}
//BrassPlus Methods
BrassPlus::BrassPlus(const char *s, long an,double bal,
double ml, double r):Brass(s,an,bal)
{
maxloan = ml;
owesBank = 0.0;
rate = r;
}
BrassPlus::BrassPlus(const Brass & ba, double ml,
double r):Brass(ba)
{
maxloan = ml;
owesBank = 0.0;
rate = r;
}
//redefine how viewacct()works
void BrassPlus::ViewAcct()const
{
//set up ###.## format
ios_base::fmtflags initalState =
cout.setf(ios_base::fixed,ios_base::floatfield);
cout.setf(ios_base::showpoint);
cout.precision(2);
Brass::ViewAcct();//display base portion
cout << "Maximum loan: $" << maxloan << endl;
cout << "Owed to bank: $" << owesBank << endl;
cout << "Loan Rate: " << 100 * rate <<
"%\n";
cout.setf(initalState);
}
//redefine how Withdraw()works
void BrassPlus::Withdraw(double amt)
{
//set up ###.## format
ios_base::fmtflags initalState =
cout.setf(ios_base::fixed,ios_base::floatfield);
cout.setf(ios_base::showpoint);
cout.precision(2);
double bal = Balance();
if (amt <= bal)
Brass::withdraw(amt);
else if (amt <= bal + maxloan - owesBank)
{
double advance = amt - bal;
owesBank += advance * (1.0 + rate);
cout << "Bank advance: $" << advance << endl;
cout << "Finance charge: $" << advance*rate << endl;
Deposit(advance);
Brass::withdraw(amt);
}
else
cout << "Credit limit exceeded.Transaction cancelled.\n";
cout.setf(initalState);
解析:
1.这里面的构造函数都使用成员初始化列表句法,将基类的信息传递给基类 的构造函数,然后使用构造函数初始化BrassPlus类新增的数据项。
2.非构造函数不能采用成员初始化列表句法,但派生类方法可以调用公有的基类方法。
记住:派生类并不能直接访问基类的私有数据,而必须使用基类的公有方法才能访问这些数据,访问的方式取决于方法。
使用Brass和BrassPlus类
程序清单 usebrass1.cpp
注意:如果在派生类中重新定义了基类的方法,则将它设置为虚方法,否则,设置为非虚方法。
如果希望同一个方法在派生类和基类中的行为是不同的,也就是说,方法的行为应取决于调用该方法的对象。这种较复杂的行为成为多态--具有多种形态,就是指同一个方法的想行为随上下文而异。有两种机制可用于实现多态公有继承:
1.在派生类中重新定义基类的方法。
2.使用虚方法。
为了说明上述性质,我们来看另一个例子:一个类用于 表示基本支票账户-Brass,另一个类用于表示代表Brass Plus支票账户,它添加了透支保护特性。也就是说,如果用户签出一张超出其存款余额的支票--但是超出的数额不是很大,银行将支付这张支票,对超出的部分收取额外的费用,并追加罚款。
下面是用于Brass支票账户的信息:
1.客户姓名
2.账户
3.当前余额
下面是可执行的操作:
1.创建账户
2.存款
3.取款
4.显示账户信息
而Brass Plus包含Brass的所有信息以及以下信息项:
1.透支上限
2.透支贷款利率
3.当前透支总额
不需要新增操作,担有两种操作的实现不同
1.对于取款操作,必须考虑透支保护
2.显示操作必须显示Brass Plus账户的其他信息
我们将第一个类命名为Brass,第二个类为BrassPlus。很明显,应从Prass公有派生出PrassPlus类。
程序清单 brass.h
//brass.h--bank account classes
#ifndef BRASS_H_
#define BRASS_H_
//Brass Account class
class Brass
{
private:
enum {MAX=35};
char fullName[MAX];
long acctNum;
double balance;
public:
Brass (const char *s = "Nullbody",long an = -1,
double bal = 0.0);
void Deposit(double amt);
virtual void withdraw(double amt);
double Balance()const;
virtual void ViewAcct()const;
virtual ~Brass(){}
};
//Brass plus Account Class
class BrassPlus: public Brass
{
private:
double maxloan;
double rate;
double owesBank;
public:
BrassPlus(const char *s = "Nullbody",long an=-1,
double bal=0.0,double ml=500,double r=0.10);
BrassPlus(const Brass & ba,double ml=500,double r=0.1);
virtual void ViewAcct()const;
virtual void Withdraw(double amt);
void ResetMax(double m){maxloan = m;}
void ReseRate(double r){rate = r;}
void ResetOwes(){owesBank = 0;}
};
#endif
对于上述程序清单,有以下说明:
BrassPlus类在Brass类的基础上添加了3个私有数据成员和3个公有成员函数。
Brass类和BrassPlus类都声明了ViewAcct()和Widthdraw()方法,但BrassPlus对象和Brass对象的这些方法的行为是不同的。
Brass类在声明ViewAcct()和Withdraw()时使用了新关键字virtual。这些方法被称为虚方法(virtual method)。
Brass还声明了一个虚拟析构函数,虽然该函数没进行任何操作。
记住:如果要在派生类中重新定义基类的方法,通常应将基类方法声明为虚拟的,这样,程序将根据对象类型而不是引用或指针的类型来选择方法版本;为基类声明一个虚拟析构函数也是一种惯例。
程序清单 brass.cpp
//brass.cpp--bank account class methods
#include <iostream>
#include <cstring>
#include "brass.h"
using std::cout;
using std::ios_base;
using std::endl;
//Brass methods
Brass::Brass(const char *s,long an,double bal)
{
std::strncpy(fullName,s,MAX - 1);
fullName[MAX - 1] - '\0';
acctNum = an;
balance = bal;
}
void Brass::Deposit(double amt)
{
if (amt < 0)
cout << "Neqative deposit not allowed: "
<< "deposit is cancelled.\n";
else
balance += amt;
}
void Brass::withdraw(double amt)
{
if (amt < 0)
cout << "Withdrawal amount must be positive: "
<< "withdrawall canceled.\n";
else if (amt <= balance)
balance -= amt;
else
cout << "Withdrawal amount of $" << amt
<< " exceeds your balance.\n"
<< "Withdrawal canceled.\n";
}
double Brass::Balance()const
{
return balance;
}
void Brass::ViewAcct()const
{
//set up ####.## format
ios_base::fmtflags initialState =
cout.setf(ios_base::fixed,ios_base::floatfield);
cout.setf(ios_base::showpoint);
cout.precision(2);
cout << "Client: " << fullName << endl;
cout << "Account Number: " << acctNum << endl;
cout << "Balance: $" << balance << endl;
cout.setf(initialState);//restore original format
}
//BrassPlus Methods
BrassPlus::BrassPlus(const char *s, long an,double bal,
double ml, double r):Brass(s,an,bal)
{
maxloan = ml;
owesBank = 0.0;
rate = r;
}
BrassPlus::BrassPlus(const Brass & ba, double ml,
double r):Brass(ba)
{
maxloan = ml;
owesBank = 0.0;
rate = r;
}
//redefine how viewacct()works
void BrassPlus::ViewAcct()const
{
//set up ###.## format
ios_base::fmtflags initalState =
cout.setf(ios_base::fixed,ios_base::floatfield);
cout.setf(ios_base::showpoint);
cout.precision(2);
Brass::ViewAcct();//display base portion
cout << "Maximum loan: $" << maxloan << endl;
cout << "Owed to bank: $" << owesBank << endl;
cout << "Loan Rate: " << 100 * rate <<
"%\n";
cout.setf(initalState);
}
//redefine how Withdraw()works
void BrassPlus::Withdraw(double amt)
{
//set up ###.## format
ios_base::fmtflags initalState =
cout.setf(ios_base::fixed,ios_base::floatfield);
cout.setf(ios_base::showpoint);
cout.precision(2);
double bal = Balance();
if (amt <= bal)
Brass::withdraw(amt);
else if (amt <= bal + maxloan - owesBank)
{
double advance = amt - bal;
owesBank += advance * (1.0 + rate);
cout << "Bank advance: $" << advance << endl;
cout << "Finance charge: $" << advance*rate << endl;
Deposit(advance);
Brass::withdraw(amt);
}
else
cout << "Credit limit exceeded.Transaction cancelled.\n";
cout.setf(initalState);
}
解析:
1.这里面的构造函数都使用成员初始化列表句法,将基类的信息传递给基类 的构造函数,然后使用构造函数初始化BrassPlus类新增的数据项。
2.非构造函数不能采用成员初始化列表句法,但派生类方法可以调用公有的基类方法。
记住:派生类并不能直接访问基类的私有数据,而必须使用基类的公有方法才能访问这些数据,访问的方式取决于方法。
使用Brass和BrassPlus类
程序清单 usebrass1.cpp
//usebrass1.cpp--testing bank account classes //compile with brass.cpp #include <iostream> #include "brass.h" int main(int argc, char * argv [ ]) { using std::cout; using std::endl; Brass Piggy("Porcelot Pigg",381299,4000.00); BrassPlus Hoggy("Horatio Hogg",382288,3000.00); Piggy.ViewAcct(); cout << endl; Hoggy.ViewAcct(); cout << endl; cout << "Depositing $1000 into the Hogg Account: \n"; Hoggy.Deposit(1000.00); cout << "New balance: $" << Hoggy.Balance() << endl; cout << "Withdrawing $4200 from the Pigg Accout: \n"; Piggy.withdraw(4200.00); cout << "Pigg accout balance $" << Piggy.Balance() << endl; c 4000 out << "Withdrawing $4200 from the Hogg Account: \n"; Hoggy.Withdraw(4200.00); Hoggy.ViewAcct(); return 0; }
注意:如果在派生类中重新定义了基类的方法,则将它设置为虚方法,否则,设置为非虚方法。
相关文章推荐
- Android Native 绘图方法
- 注册表的组织结构
- PostgreSQL教程(三):表的继承和分区表详解
- Ruby on Rails所构建的应用程序基本目录结构总结
- C#中struct和class的区别详解
- Lua面向对象之类和继承浅析
- 浅析Ruby中继承和消息的相关知识
- SQLSERVER的非聚集索引结构深度理解
- 调整SQLServer2000运行中数据库结构
- C#基础语法:结构和类区别详解
- 深入c# 类和结构的区别总结详解
- c#结构和类的相关介绍
- VBS ArrayList Class vbs中的数组类
- 大家看了就明白了css样式中类class与标识id选择符的区别小结
- C#中结构(struct)的部分初始化和完全初始化实例分析
- 设计引导--一个鸭子游戏引发的设计理念(多态,继承,抽象,接口,策略者模式)
- C#中类与结构的区别实例分析
- C#枚举类型与结构类型实例解析
- javascript实现表现、结构、行为分离的选项卡效果!
- 公文结构