类继承:它能够从已有的类派⽣出新的类,⽽派⽣类继承了原有类(称为基类)的特征,包括⽅法。
string类有⼀个将const char *作为 参数的构造函数,使⽤C-⻛格字符串初始化string对象时,将⾃动调⽤这 个构造函数
class Son : public Father
{
...
};
构造函数必须给新成员(如果有的话)和继承的成员提供数据。
情况1:
//Father:TableTennisPlayer
//Son:RatedPlayer
RatedPlayer::RatedPlayer(unsigned int r , const string& fn ,
const string& ln , bool ht ):TableTennisPlayer(fn,ln,ht)
{
rating = r;
}
其中:TableTennisPlayer(fn,ln,ht)
是成员初始化列表。它是可执⾏的 代码,调⽤TableTennisPlayer构造函数。
情况2:
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer& tp)
:TableTennisPlayer(tp)
{
rating = r;
}
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer& tp)
:TableTennisPlayer(tp),rating(t)
{
}
有关派⽣类构造函数的要点如下:
RatedPlayer::RatedPlayer
(unsigned int r , const string& fn ,
const string& ln , bool ht )
{
rating = r;
}
上述等效于:
RatedPlayer::RatedPlayer(unsigned int r , const string& fn ,
const string& ln , bool ht )//:TabTennisPlayer()
{
rating = r;
}
调用派生类构造函数进行初始化:
Son::Son(type1 x, type2 y):Father(x,y)
{
...
}
其中Son是派⽣类,Father是基类,x和y是基类构造函数使⽤的变量。
示例:
tabtenn.h
#pragma once
//tabtenn.h -- a table-tennis base class
#ifndef TABTENN1_H_
#define TABTENN1_H_
#include<string>
using std::string;
//simple base class
class TableTennisPlayer
{
private:
string firstname;
string lastname;
bool hasTable;
public:
TableTennisPlayer(const string& fn = "none", const string& ln = "none", bool ht = false);
void Name()const;
bool HasTable()const { return hasTable; };
void ResetTable(bool v) { hasTable = v; };
};
//simple derived class
class RatedPlayer :public TableTennisPlayer
{
private:
unsigned int rating;
public:
RatedPlayer(unsigned int r=0, const string& fn = "none", const string& ln = "none", bool ht = false);
RatedPlayer(unsigned int r ,const TableTennisPlayer& tp);
unsigned int Rating()const { return rating; }
void ResetRating(unsigned int r) { rating = r; }
};
#endif // !TABTENN1_H_
tabtenn1.cpp
//tabtenn1.cpp -- simple base-class methods
#include"tabtenn1.h"
#include<iostream>
//TableTennisPlayer
TableTennisPlayer::TableTennisPlayer(const string& fn , const string& ln , bool ht )
:firstname(fn),lastname(ln),hasTable(ht)//注意默认参数
{
}
void TableTennisPlayer::Name()const
{
std::cout << this->lastname << ", " << this->firstname ;
}
//RatePlayer
RatedPlayer::RatedPlayer(unsigned int r , const string& fn, const string& ln, bool ht)
:TableTennisPlayer(fn,ln,ht)
{
rating = r;
}
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer& tp)
:TableTennisPlayer(tp),rating(r)
{
}
usett1.cpp
//usett1.cpp -- using base class and derived class
#include"tabtenn1.h"
#include<iostream>
int main()
{
using std::cout;
using std::endl;
TableTennisPlayer player1("Tara", "Boomdea", false);
RatedPlayer rplayer1(1140, "Mallory", "Duck", true);
rplayer1.Name();
if (rplayer1.HasTable())
cout << ":has a table.\n";
else
cout << ":hasn't a table.\n";
player1.Name();
if (player1.HasTable())
cout << ":has a table.\n";
else
cout << ":hasn't a table.\n";
cout << "Name: ";
rplayer1.Name();
cout << "; Rating: " << rplayer1.Rating() << endl;
//initialize RatedPlayer using TableTennisPlayer object
RatedPlayer rplayer2(1212, player1);
cout << "Name: ";
rplayer2.Name();
cout << "; Rating: " << rplayer2.Rating() << endl;
return 0;
}
```c++
void Show(const TableTennisPlayer& rt)
{
using std::cout;
cout<<"Name: ";
rt.Name();
cout<<"\nTable: ";
if(rt.HasTable())
cout<<"yes\n";
else
cout<<"no\n";
}
//形参rt是⼀个基类引⽤,它可以指向基类对象或派⽣类对象,所以可以在Show( )中使⽤TableTennis参数或Ratedplayer参数:
TableTennisPlayer player1("Tara", "Boomdea", false);
RatedPlayer rplayer1(1140, "Mallory", "Duck", true);
show(player1);
show(rplayer1);
```c++
void Wohs(const TableTennisPlayer* pt);
...
TableTennisPlayer player1("Tara", "Boomdea", false);
RatedPlayer rplayer1(1140, "Mallory", "Duck", true);
Wohs(&player1);
Wohs(&rplayer1);
```c++
RatedPlayer olaf1(1840,"Olaf","Loaf",true);
TableTennisPlayer olaf2(olaf1);
//它将olaf2初始化为嵌套在RatedPlayer对象olaf1中的TableTennisPlayer对象。
//要初始化olaf2,匹配的构造函数的原型如下:
TableTennisPlayer(const RatedPlayer&);
//类定义中没有这样的构造函数,但存在隐式复制构造函数:
//implicit copy constructor
TableTennisPlayer(const TableTennisPlayer&)
//形参是基类引⽤,因此它可以引⽤派⽣类.将olaf2初始化为olaf1时,将要使⽤该构造函数,它复制firstname、lastname和hasTable成员。
//也可以将派⽣对象赋给基类对象
RatedPlayer olaf1(1840,"Olaf","Loaf",true);
TableTennisPlayer winner;
winner=olaf1;
//程序将使⽤隐式重载赋值运算符:
TableTennisPlayer& operator=(const TableTennisPlayer& )const;
//基类引⽤指向的也是派⽣类对象,因此olaf1的基类部分被复制给winner。
基类指针或引⽤只能⽤于调⽤基类⽅法;不可以将基类对象和地 址赋给派⽣类引⽤和指针:
brass.h
#pragma once
#ifndef BRASSH
#define BRASSH
#include<string>
//Brass Account Class
class Brass
{
private:
std::string fullName;
long acctNum;
double balance;
public:
Brass(const std::string& s = "Nullbody", long an = -1, double bal = 0.0);
void Deposit(double amt);
virtual void Withdraw(double amt);
double Blance()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 std::string& s = "Nullbody", long an = -1, double bal = 0.0, double ml = 500, double r = 0.11125);
BrassPlus(const Brass& ba, double ml = 500, double r = 0.11125);
virtual void ViewAcct()const;
virtual void Withdraw(double amt);
void ResetMax(double m) { maxLoan = m; }
void ResetRate(double r) { rate = r; }
void ResetOwes() { owesBank = 0; }
};
#endif // !BRASSH
```c++
Brass dom("Dominic Banker",11224,4183.45);
BrassPlus dot("Dorothy Banker",12118.2592.00);
dom.ViewAcct(); //use Brass::ViewAcct( )
dot.ViewAcct(); //use BrassPlus::ViewAcct( )
//如果ViewAcct( )不是虚的,则程序的⾏为如下:
Brass dom("Dominic Banker",11224,4183.45);
BrassPlus dot("Dorothy Banker",12118.2592.00);
Brass & b1_ref=dom;
Brass& b2_ref=dot;
b1_ref.ViewAcct(); //use Brass::ViewAcct()
b2_ref.ViewAcct(); //use Bbrass::ViewAcct()
//使⽤Brass指针代替引⽤时,⾏为将与此类似。
//如果ViewAcct( )是虚的,则⾏为如下:
Brass dom("Dominic Banker",11224,4183.45);
BrassPlus dot("Dorothy Banker",12118.2592.00);
Brass & b1_ref=dom;
Brass& b2_ref=dot;
b1_ref.ViewAcct(); //use Brass::ViewAcct()
b2_ref.ViewAcct(); //use BbrassPlus::ViewAcct()
//使⽤Brass指针代替引⽤时,⾏为将与此类似。
brass.cpp
//brass.cpp -- bank class methods
#include<iostream>
#include"brass.h"
using std::cout;
using std::endl;
using std::string;
//formatting stuff
typedef std::ios_base::fmtflags format;
typedef std::streamsize precis;
format setFormat();
void restore(format f, precis p);
//Brass methods
Brass::Brass(const std::string& s, long an, double bal)
{
fullName = s;
acctNum = an;
balance = bal;
}
void Brass::Deposit(double amt)
{
if (amt < 0)
{
cout << "Negative deposit not allowed; "
<< "deposit is cancelled.\n";
}
else
{
balance += amt;
}
}
void Brass::Withdraw(double amt)
{
//set up ###.## format
format initialState = setFormat();
precis prec = cout.precision(2);
if (amt < 0)
{
cout << "Withdrawal amount must e positive; "
<< "withdrawal canceled.\n";
}
else if (amt <= balance)
{
balance -= amt;
}
else
{
cout << "Withdrawal amount of $" << amt
<< " exceeds your alance.\n"
<< "Withdrawal canceled.\n";
}
restore(initialState, prec);
}
double Brass::Balance()const
{
return balance;
}
void Brass::ViewAcct()const
{
//set up ###.## format
format initialState = setFormat();
precis prec = cout.precision(2);
cout << "Client: " << fullName << endl;
cout << "Account Numbber: " << acctNum << endl;
cout << "Balance: $" << balance << endl;
restore(initialState, prec); //restore original format
}
//BrassPlus Methods
//派⽣类构造函数在初始化基类私有数据时,采⽤的是成员初始化列表语法。
//都使⽤成员初始化列表语法,将基类信息传递给基类构造函数,然后使⽤构造函数体初始化BrassPlus类新增的数据项。
BrassPlus::BrassPlus(const std::string& 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) //use implicit copy constructor
{
maxLoan = ml;
owesBank = 0.0;
rate = r;
}
//⾮构造函数不能使⽤成员初始化列表语法,但派⽣类⽅法可以调⽤公有的基类⽅法。
void BrassPlus::ViewAcct()const
{
format initialState = setFormat();
precis prec = cout.precision(2);
//BrassPlus::ViewAcct( )显⽰新增的BrassPlus数据成员,并调⽤基类⽅法Brass::ViewAcct( )来显⽰基类数据成员。
//在派⽣类⽅法中,标准技术是使⽤作⽤域解析运算符来调⽤基类⽅法。
Brass::ViewAcct(); //display base portion
//如果代码没有使⽤作⽤域解析运算符,编译器将认为ViewAcct( )是BrassPlus::ViewAcct( ),这将创建⼀个不会终⽌的递归函数。
cout << "Maximum loan: $" << maxLoan << endl;
cout << "Owed to bank: $" << owesBank << endl;
cout.precision(3);
cout << "Loan Rate: " << 100 * rate << "%\n";
restore(initialState, prec);
}
//该⽅法使⽤基类的Balance( )函数来确定结余。因为派⽣类没有重新定义该⽅法,代码不必对Balance( )使⽤作⽤域解析运算符。
void BrassPlus::Withdraw(double amt)
{
//set up ###.## format
format initialState = setFormat();
precis prec = 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";
restore(initialState,prec);
}
//⽅法ViewAcct( )和Withdraw( )使⽤格式化⽅法setf( )和precision( )将浮点值的输出模式设置为定点,即包含两位⼩数。
//设置模式后,输出的模式将保持不变,因此该⽅法将格式模式重置为调⽤前的状态。
//为避免代码重复,该程序将设置格式的代码放在辅助函数中
//函数setFormat( )设置定点表⽰法并返回以前的标记设置:
format setFormat()
{
//set up ###.## format
return cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
}
//函数restore( )重置格式和精度:
void restore(format f, precis p)
{
cout.setf(f, std::ios_base::floatfield);
cout.precision(p);
}
usebrass1.cpp
//usebrass1.cpp -- testing bank account classes
//compile with brass.cpp
#include<iostream>
#include"brass.h"
int main()
{
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 Account:\n";
Piggy.Withdraw(4200.00);
cout << "Pigg account balance: $" << Piggy.Balance() << endl;
cout << "Withdrawing $4200 from the Hogg Account:\n";
Hoggy.Withdraw(4200.00);
Hoggy.ViewAcct();
return 0;
}
//请注意为何Hogg受透⽀限制,⽽Pigg没有
输出结果:
Client: Porcelot Pigg
Account Numbber: 381299
Balance: $4000.00
Client: Horatio Hogg
Account Numbber: 382288
Balance: $3000.00
Maximum loan: $500.00
Owed to bank: $0.00
Loan Rate: 11.125%
Depositing $1000 into the Hogg Account:
New balance: $4000
Withdrawing $4200 from the Pigg Account:
Withdrawal amount of $4200.00 exceeds your alance.
Withdrawal canceled.
Pigg account balance: $4000
Withdrawing $4200 from the Hogg Account:
Bank advance: $200.00
Finance charge: $22.25
Client: Horatio Hogg
Account Numbber: 382288
Balance: $0.00
Maximum loan: $500.00
Owed to bank: $222.25
Loan Rate: 11.125%
usebrass2.cpp
//usebrass2.cpp -- polymorphic example
//compile with brass.cpp
#include<iostream>
#include<string>
#include"brass.h"
const int CLIENTS = 4;
int main()
{
using std::cout;
using std::endl;
using std::cin;
Brass* p_clients[CLIENTS];
std::string temp;
long tempnum;
double tempbal;
char kind;
for (int i = 0; i < CLIENTS; i++)
{
cout << "Enter client's name:";
getline(cin, temp);
cout << "Enter client's account number: ";
cin >> tempnum;
cout << "Enter opening balance: $";
cin >> tempbal;
cout << "Enter 1 for Brass Account or "
<< "2 for BbrassPlus Account: ";
while (cin >> kind && (kind != '1' && kind != '2'))
cout << "Enter either 1 or 2: ";
if (kind == '1')
{
p_clients[i] = new Brass(temp,tempnum,tempbal);
}
else
{
double tmax, trate;
cout << "Enter the overdraft limit: $";
cin >> tmax;
cout << "Enter the interest rate "
<< "as a decimal fraction: ";
cin >> trate;
p_clients[i] = new BrassPlus(temp, tempnum, tempbal, tmax, trate);
}
while (cin.get() != '\n')
continue;
}
cout << endl;
//多态性是由下述代码提供的:
for (int i = 0; i < CLIENTS; i++)
{
p_clients[i]->ViewAcct();
cout << endl;
}
//如果数组成员指向的是Brass对象,则调⽤Brass::ViewAcct( );
//如果指向的是BrassPlus对象,则调⽤BrassPlus::ViewAcct( )。
//如果Brass::ViewAcct( )被声明为非虚的,则在任何情况下都将调⽤Brass::ViewAcct( )。
for (int i = 0; i < CLIENTS; i++)
{
delete p_clients[i]; //free memory
}
cout << "Done.\n";
return 0;
}
输出结果:
Enter client's name:Harry Fishsong
Enter client's account number: 112233
Enter opening balance: $1500
Enter 1 for Brass Account or 2 for BbrassPlus Account: 1
Enter client's name:Dinah Otternoe
Enter client's account number: 121213
Enter opening balance: $18800
Enter 1 for Brass Account or 2 for BbrassPlus Account: 2
Enter the overdraft limit: $350
Enter the interest rate as a decimal fraction: 0.12
Enter client's name:Brenda Birdherd
Enter client's account number: 212118
Enter opening balance: $5200
Enter 1 for Brass Account or 2 for BbrassPlus Account: 2
Enter the overdraft limit: $800
Enter the interest rate as a decimal fraction: 0.10
Enter client's name:Tim Turtletop
Enter client's account number: 233255
Enter opening balance: $688
Enter 1 for Brass Account or 2 for BbrassPlus Account: 1
Client: Harry Fishsong
Account Numbber: 112233
Balance: $1500.00
Client: Dinah Otternoe
Account Numbber: 121213
Balance: $18800.00
Maximum loan: $350.00
Owed to bank: $0.00
Loan Rate: 12.000%
Client: Brenda Birdherd
Account Numbber: 212118
Balance: $5200.00
Maximum loan: $800.00
Owed to bank: $0.00
Loan Rate: 10.000%
Client: Tim Turtletop
Account Numbber: 233255
Balance: $688.00
Done.
double x=2.5;
int * pi= &x; //invalid assignment, mismatched pointer types
int& r1= x; //invalid assignment, mismatched reference type
指向基类的引⽤或指针可以引⽤派⽣类对象,⽽不必进⾏显式类型转换。例如,下⾯的初始化是允许的:
BrassPlus dilly("Annie Dill",493222,2000);
Brass* pb=&dilly; //ok
Brass& rb=dilly; //ok
//假定每个函数都调⽤虚⽅法ViewAcct( ):
void fr(Brass& rb);
void fp(Brass* pb);
void fv(Brass b);
int main()
{
Brass b("Billy Bee", 123432, 10000.0);
BrassPlus bp("Betty Beep", 232313, 12345.0);
//随引⽤和指针发⽣的隐式向上转换导致函数fr( )和fp( )分别为Brass对象和BrassPlus对象使⽤Brass::ViewAcct( )和BrassPlus::ViewAcct( )。
fr(b); //uses Brass::ViewAcct();
fr(bp); //uses BrassPlus::ViewAcct();
fp(b); //uses Brass::ViewAcct();
fp(bp); //uses BrassPlus::ViewAcct();
//按值传递导致只将BrassPlus对象的Brass部分传递给函数fv( )。
fv(b); //uses Brass::ViewAcct();
fv(bp); //uses Brass::ViewAcct();
...
}
向上强制转换和向下强制转换
BrassPlus ophelia; //drived-class object
Brass * bp; //base-class pointer
bp=&ophelia; //Brass pointer to BrasPlus object
bp->ViewAcct();
如果要在派⽣类中重新定义基类的⽅法,则将它设置为虚⽅法;否则,设置为⾮虚⽅法。
例如:
class Dwelling
{
public:
virtual void showperks(int a) const;
...
};
class Hovel:public Dwelling
{
public:
//新定义将showperks( )定义为⼀个不接受任何参数的函数。
virtual void showperks()const;
//重新定义不会⽣成函数的两个重载版本,⽽是隐藏了接受⼀个int参数的基类版本。
...
};
//这将导致问题,可能会出现类似于下⾯这样的编译器警告:
Waring:Havel::showperks(void) hides Dwelling::showperks(int)
//也可能不会出现警告。但不管结果怎样,代码将具有如下含义:
Hovel trump;
trump.showperks(); //valid
trump.showperks(5); //invalid
- 如果**返回类型**是**基类引⽤或指针**,则**可以修改**为**指向派⽣类的引⽤或指针**(这种例外是新出现的)。这种特性被称为返回类型协变(covariance of return type),因为允许返回类型随类类型的变化⽽变化。
- 注意,这种例外只适⽤于**返回值**,⽽不适⽤于参数。
- 如果**只重新定义⼀个版本**,则**另外两个版本将被隐藏**,派⽣类对象将⽆法使⽤它们。
- 注意,如果不需要修改,则新定义可**只调⽤基类版本**
```c++
void Hovel::showperks()const{Dwelling::showperks();}
最好对类数据成员采⽤私有访问控制,不要使⽤保护访问控制;同时通过基类⽅法使派⽣类能够访问基类数据。
class BaseEllipse //ABC
{
private:
double x;
double y;
...
public:
BaseEllipse(double x0=0,double y0=0):x(x0),y(y0){}
virtual ~BaseEllipse(){}
//,也许所有的基类⽅法都与Move( )⼀样,可以在基类中进⾏定义,但您仍需要将这个类声明为抽象的。
void Move(int nx,int ny){x=nx; y=ny;}
//在这种情况下,可以将原型声明为虚的:
// void Move(int nx,int ny)=0;
//这将使基类成为抽象的,但您仍可以在实现⽂件中提供⽅法的定义:
// void BaseEllipse::Move(int nx,int ny){x=nx; y=ny;}
virtual double Area()const=0;
//在原型中使⽤=0指出类是⼀个抽象基类,在类中可以不定义该函数。
...
};
virtual double Area()const=0;
//原型中的=0使虚函数成为纯虚函数。
//C++允许纯虚函数有定义
acctabc.h
#pragma once
//为帮助派⽣类访问基类数据,AcctABC提供了⼀些保护⽅法;
//派⽣类⽅法可以调⽤这些⽅法,但它们并不是派⽣类对象的公有接⼝的组成部分。
//AcctABC还提供⼀个保护成员函数,⽤于处理格式化(以前是使⽤⾮成员函数处理的)。
//另外,AcctABC类还有两个纯虚函数,所以它确实是抽象类。
//acctabc.h -- bank account classes
#ifndef ACCTABC_H_
#define ACCTABC_H_
#include<iostream>
#include<string>
//Abstract Base Class
class AcctABC
{
private:
std::string fullName;
long acctNum;
double balance;
protected:
//这个版本定义了⼀个结构,⽤于存储两项格式设置;并使⽤该结构来设置和恢复格式,
struct Formatting
{
std::ios_base::fmtflags flag;
std::streamsize pr;
};
//保护⽅法FullName( )和AcctNum( )提供了对数据成员fullName和acctNum的只读访问,使得可以进⼀步定制每个派⽣类的ViewAcct( )。
const std::string& FullName()const { return fullName; }
long AcctNum()const { return acctNum; }
//只需调⽤两个函数来设置和恢复格式
// Formatting f = SetFormat();
// ...
// Restore(f);
Formatting SetFormat()const;
void Restore(Formatting& f)const;
public:
AcctABC(const std::string& s = "Nullbody", long an = -1, double bal = 0.0);
void Deposit(double amt);
virtual void Withdraw(double amt) = 0;
double Balance()const { return balance; }
virtual void ViewAcct()const = 0;
virtual ~AcctABC(){}
};
//Brass Account Class
class Brass :public AcctABC
{
public:
Brass(const std::string& s="Nullbody",long an=-1,double bal=0.0)
:AcctABC(s,an,bal){}
virtual void Withdraw(double amt);
virtual void ViewAcct()const;
virtual ~Brass(){}
};
//BrassPlus Account Class
class BrassPlus :public AcctABC
{
private:
double maxLoan;
double rate;
double owesBank;
public:
BrassPlus(const std::string& s = "Nullbody", long an = -1, double bal = 0.0, double ml = 500, double r = 0.0);
BrassPlus(const Brass& ba, double ml = 500, double r = 0.10);
virtual void ViewAcct()const;
virtual void Withdraw(double amt);
void ResetMax(double m) { maxLoan = m; }
void ResetRate(double r) { rate = r; }
void ResetOwes() { owesBank = 0; }
};
#endif // !ACCTABC_H_
acctabc.cpp
//acctabc.cpp -- bank account class methods
#include<iostream>
#include"acctabc.h"
using std::cout;
using std::ios_base;
using std::endl;
using std::string;
//Abstract Base Class
AcctABC::AcctABC(const std::string& s, long an, double bal)
{
fullName = s;
acctNum = an;
balance = bal;
}
void AcctABC::Deposit(double amt)
{
if (amt < 0)
{
cout << "Negative deposit not allowed; "
<< "deposit is cancelled.\n";
}
else
{
balance += amt;
}
}
void AcctABC::Withdraw(double amt)
{
balance -= amt;
}
//protected methods for formatting
AcctABC::Formatting AcctABC::SetFormat()const
{
//set up ###.## format
Formatting f;
f.flag=cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
f.pr = cout.precision(2);
return f;
}
void AcctABC::Restore(Formatting& f)const
{
cout.setf(f.flag, ios_base::floatfield);
cout.precision(f.pr);
}
//Brass methods
void Brass::Withdraw(double amt)
{
if (amt < 0)
{
cout << "Withdrawal amount must e positive; "
<< "withdrawal canceled.\n";
}
else if (amt <= Balance())
{
AcctABC::Withdraw(amt);
}
else
{
cout << "Withdrawal amount of $" << amt
<< " exceeds your alance.\n"
<< "Withdrawal canceled.\n";
}
}
void Brass::ViewAcct()const
{
Formatting f = SetFormat();
cout << "Brass Client:" << FullName() << endl;
cout << "Account Number: " << AcctNum() << endl;
cout << "Balance: $" << Balance() << endl;
Restore(f);
}
//BrassPlus Methods
BrassPlus::BrassPlus(const std::string& s, long an, double bal, double ml, double r)
:AcctABC(s,an,bal)
{
maxLoan = ml;
owesBank = 0.0;
rate = r;
}
BrassPlus::BrassPlus(const Brass& ba, double ml, double r)
:AcctABC(ba)
{
maxLoan = ml;
owesBank = 0.0;
rate = r;
}
void BrassPlus::ViewAcct()const
{
Formatting f = SetFormat();
cout << "BrassPlus Client: " << FullName() << endl;
cout << "Account Number: " << AcctNum() << endl;
cout << "Balance: $" << Balance() << endl;
cout << "Maximum loan: $" << maxLoan << endl;
cout << "Owed to bank: $" << owesBank << endl;
cout.precision(3);
cout << "Loan Rate: " << 100 * rate << "%\n";
Restore(f);
}
void BrassPlus::Withdraw(double amt)
{
Formatting f = SetFormat();
double bal = Balance();
if (amt <= bal)
AcctABC::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);
AcctABC::Withdraw(amt);
}
else
cout << "Credit limit exceeded. Transaction cancelled.\n";
Restore(f);
}
usebrass3.cpp
//usebrass3.cpp -- polymorphic example using an abstract base class
//compile with acctabc.cpp
#include<iostream>
#include<string>
#include"acctabc.h"
const int CLIENTS = 4;
int main()
{
using std::cin;
using std::cout;
using std::endl;
AcctABC* p_clientsCLIENTS;
std::string temp;
long tempnum;
double tempbal;
char kind;
for (int i = 0; i < CLIENTS; i++)
{
cout << "Enter client's name: ";
getline(cin, temp);
cout << "Enter client's account number: ";
cin >> tempnum;
cout << "Enter opening balance: $";
cin >> tempbal;
cout << "Enter 1 for Brass Account or 2 for BrassPlus Account: ";
while (cin >> kind && (kind != '1' && kind != '2'))
cout << "Enter either 1or 2: ";
if (kind == '1')
{
p_clientsi = new Brass(temp, tempnum, tempbal);
}
else
{
double tmax, trate;
cout << "Enter the overdraft limit: $";
cin >> tmax;
cout << "Enter the interest rate as a decimal fraction: ";
cin >> trate;
p_clientsi = new BrassPlus(temp, tempnum, tempbal, tmax, trate);
}
while (cin.get() != '\n')
continue;
}
cout << endl;
for (int i = 0; i < CLIENTS; i++)
{
p_clientsi->ViewAcct();
cout << endl;
}
for (int i = 0; i < CLIENTS; i++)
{
delete p_clientsi;
}
cout << "Done.\n";
return 0;
}
//该程序本⾝的⾏为与⾮抽象基类版本相同,因此如果输⼊与给程序usebrass2.cpp提供的输⼊相同,输出也将相同。
//Base Class Using DMA
class baseDMA
{
private:
char* label; //use new in constructors
int rating;
public:
baseDMA(const char* l = "null", int r = 0);
//复制构造函数
baseDMA(const baseDMA& rs);
//析构函数
virtual ~baseDMA();
//重载赋值运算符
baseDMA& operator=(const baseDMA& rs);
...
};
//drived class without DMA
class lacksDMA :public baseDMA
{
private:
char color[40];
public:
...
};
//不需要为lackDMA类定义显式析构函数、复制构造函数和赋值运算符。
//derived class with DMA
class hasDMA :public baseDMA
{
private:
char* style; //use new in constructors
public:
...
};
//hasDMA析构函数必须释放指针style管理的内存,并依赖于baseDMA的析构函数来释放指针label管理的内存
baseDMA::~baseDMA()
{
delete [] label;
}
hasDMA::~hasDMA()
{
delete [] style;
}
//BaseDMA的复制构造函数遵循⽤于char数组的常规模式
//即使⽤strlen( )来获悉存储C-⻛格字符串所需的空间、分配⾜够的内存(字符数加上存储空字符所需的1字节)并使⽤函数strcpy()将原始字符串复制到⽬的地:
baseDMA::baseDMA(const baseDMA& rs)
{
label=new char[std::strlen(rs.label)+1];
std::strcpy(label,rs.label);
rating=rs.rating;
}
//hasDMA复制构造函数只能访问hasDMA的数据
//它必须调⽤baseDMA复制构造函数来处理共享的baseDMA数据:
hasDMA::hasDMA(const hasDMA& rs)
:baseDMA(hs) //成员初始化列表将⼀个hasDMA引⽤传递给baseDMA构造函数。
{
//因为复制构造函数baseDMA有⼀个baseDMA引⽤参数,⽽基类引⽤可以指向派⽣类型。
//因此,baseDMA复制构造函数将使⽤hasDMA参数的baseDMA部分来构造新对象的baseDMA部分。
style=new char[std::strlen(hs.style)+1];
std::strcpy(style,hs.style);
}
baseDMA& baseDMA::operator=(const baseDMA& rs)
{
if(this==&rs)
return *this;
delete [] label;
label=new char[std::strlen(rs.label)+1];
std::strcpy(label,rs.label);
rating=rs.rating;
return *this;
}
//由于hasDMA也使⽤动态内存分配,所以它也需要⼀个显式赋值运算符。
//派⽣类的显式赋值运算符必须负责所有继承的baseDMA基类对象的赋值,可以通过显式调⽤基类赋值运算符来完成这项⼯作
hasDMA& hasDMA::operator=(const hasDMA& hs)
{
if(this==&hs)
return *this;
//通过使⽤函数表⽰法,⽽不是运算符表⽰法,可以使⽤作⽤域解析运算符。
//该语句的含义: *this=hs; //use baseDMA::operator=()
hasDMA::operator=(hs); //copy base portion
//使⽤函数表⽰法使得赋值运算符被正确调⽤。
delete [] style;
style= new char[std::strlen(hs.style)+1];
std::strcpy(style,hs.style);
return *this;
}
dma.h
#pragma once
#ifndef DMA_H_
#define DMA_H_
#include<iostream>
//Base Class Using DMA
class baseDMA
{
private:
char* label;
int rating;
public:
baseDMA(const char* l = "null", int r = 0);
baseDMA(const baseDMA& rs);
virtual ~baseDMA();
baseDMA& operator=(const baseDMA& rs);
friend std::ostream& operator<<(std::ostream& os, const baseDMA& rs);
};
//drived class without DMA
//no destructor needed
//uses implicit copy constructor
//uses implicit assignment operator
class lacksDMA :public baseDMA
{
private:
enum{COL_LEN=40};
char color[COL_LEN];
public:
lacksDMA(const char* c = "blank", const char* l = "null", int r = 0);
lacksDMA(const char* c, const baseDMA& rs);
friend std::ostream& operator<<(std::ostream& os, const lacksDMA& rs);
};
//derived class with DMA
class hasDMA :public baseDMA
{
private:
char* style; //use new in constructors
public:
hasDMA(const char* s = "none", const char* l = "null", int r = 0);
hasDMA(const char* s, const baseDMA& rs);
hasDMA(const hasDMA& hs);
~hasDMA();
hasDMA& operator=(const hasDMA& rs);
friend std::ostream& operator<<(std::ostream& os, const hasDMA& rs);
};
#endif // !DMA_H_
dma.cpp
//dma.cpp -- dma class methods
#pragma warning(disable : 4996)
#include "dma.h"
#include<cstring>
//baseDMA methods
baseDMA::baseDMA(const char* l , int r )
{
label = new char[std::strlen(l) + 1];
std::strcpy(label, l);
rating = r;
}
baseDMA::baseDMA(const baseDMA& rs)
{
label = new char[std::strlen(rs.label) + 1];
std::strcpy(label, rs.label);
rating = rs.rating;
}
baseDMA::~baseDMA()
{
delete[] label;
}
baseDMA& baseDMA::operator=(const baseDMA& rs)
{
if (this == &rs)
return *this;
delete[] label;
label = new char[std::strlen(rs.label) + 1];
strcpy(label, rs.label);
rating = rs.rating;
return *this;
}
std::ostream& operator<<(std::ostream& os, const baseDMA& rs)
{
os << "Label: " << rs.label << std::endl;
os << "Rating: " << rs.rating << std::endl;
return os;
}
//lacksDMA methods
lacksDMA::lacksDMA(const char* c, const char* l, int r)
:baseDMA(l,r)
{
std::strncpy(color, c, 39);
color[39] = '\0';
}
lacksDMA::lacksDMA(const char* c, const baseDMA& rs)
:baseDMA(rs)
{
std::strncpy(color, c, COL_LEN - 1);
color[COL_LEN - 1] = '\0';
}
std::ostream& operator<<(std::ostream& os, const lacksDMA& ls)
{
os << (const baseDMA&)ls;
os << "Color: " << ls.color << std::endl;
return os;
}
//hasDMA methods
hasDMA::hasDMA(const char* s, const char* l, int r)
:baseDMA(l,r)
{
style = new char[std::strlen(s) + 1];
std::strcpy(style, s);
}
hasDMA::hasDMA(const char* s, const baseDMA& rs)
:baseDMA(rs)
{
style = new char[std::strlen(s) + 1];
std::strcpy(style, s);
}
hasDMA::hasDMA(const hasDMA& hs)
:baseDMA(hs)
{
style = new char[std::strlen(hs.style) + 1];
strcpy(style, hs.style);
}
hasDMA::~hasDMA()
{
delete[] style;
}
hasDMA& hasDMA::operator=(const hasDMA& hs)
{
if (this == &hs)
return *this;
baseDMA::operator=(hs);
delete[] style;
style = new char[std::strlen(hs.style) + 1];
strcpy(style, hs.style);
return *this;
}
std::ostream& operator<<(std::ostream& os, const hasDMA& hs)
{
//作为hasDMA类的友元,该函数能够访问style成员。
//使⽤baseDMA类的友元函数operator<<( ),访问baseDMA类成员lable和rating
//解决⽅法是使⽤强制类型转换,以便匹配原型时能够选择正确的函数。
//友元不是成员函数,所以不能使⽤作⽤域解析运算符来指出要使⽤哪个函数。
os << (const baseDMA&) hs;//代码将参数const hasDMA &转换成类型为const baseDMA& 的参数:
os << "Style: " << hs.style << std::endl;
return os;
}
usedma.cpp
//usedma.cpp -- inheritance,friends, and DMA
//compile with dma.cpp
#include<iostream>
#include"dma.h"
int main()
{
using std::cout;
using std::endl;
baseDMA shirt("Portabelly", 8);
lacksDMA balloon("red", "Blimpo", 4);
hasDMA map("Mercator", "Buffalo Keys", 5);
cout << "Displaying baseDMA object:\n";
cout << shirt << endl;
cout << "Displaying lacksDMA object:\n";
cout << balloon << endl;
cout << "Display hasDMA object:\n";
cout << map << endl;
lacksDMA balloon2(balloon);
cout << "Result of lacksDMA copy:\n";
cout << balloon2 << endl;
hasDMA map2;
map2 = map;
cout << "Result of hasDMA assignment:\n";
cout << map2 << endl;
return 0;
}
输出结果:
Displaying baseDMA object:
Label: Portabelly
Rating: 8
Displaying lacksDMA object:
Label: Blimpo
Rating: 4
Color: red
Display hasDMA object:
Label: Buffalo Keys
Rating: 5
Style: Mercator
Result of lacksDMA copy:
Label: Blimpo
Rating: 4
Color: red
Result of hasDMA assignment:
Label: Buffalo Keys
Rating: 5
Style: Mercator
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。