基类与派生类
(1)派生类对象存储了基类的数据成员
(2)派生类可以使用基类的方法
(3)派生类需要自己的构造函数
(4)派生类可以根据需要添加额外的数据成员和成员函数。
构造函数的考虑:
派生类不能直接访问基类的私有成员,而必须通过基类方法进行访问。
创建派生类对象时,程序首先创建基类对象。
派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数,派生类构造函数应初始化派生类新增的数据成员。
创建派生类对象时,程序 首先调用基类构造函数,因此将使用隐式构造函数。
派生类对象过期时,程序首先调用派生类析构函数,然后调用基类析构函数。
派生类与基类有一些特殊的关系。派生类对象可以使用基类的方法。
另外 两个重要关系是 基类指针(基类引用)在不进行显示类型转换的情况下指向派生类对象。
多态公有继承
有两种方法可以实现定义基类的方法:
1.在派生类中重新定义基类的方法。
2.使用虚方法。
开发brass类和brassplus类:
// brass.h -- bank account classes
#ifndef BRASS_H_
#define BRASS_H_
#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 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 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; }
};
// brass.cpp -- bank account 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 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 be positive; "
<< "withdrawal canceled.\n";
else if (amt <= balance)
balance -= amt;
else
cout << "Withdrawal amount of $" << amt
<< " exceeds your balance.\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 Number: " << acctNum << endl;
cout << "Balance: $" << balance << endl;
restore(initialState, prec); // Restore original format
}
// BrassPlus Methods
BrassPlus::BrassPlus(const 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) // uses implicit copy constructor
{
maxLoan = ml;
owesBank = 0.0;
rate = r;
}
// redefine how ViewAcct() works
void BrassPlus::ViewAcct() const
{
// set up ###.## format
format initialState = setFormat();
precis prec = cout.precision(2);
Brass::ViewAcct(); // display base portion
cout << "Maximum loan: $" << maxLoan << endl;
cout << "Owed to bank: $" << owesBank << endl;
cout.precision(3); // ###.### format
cout << "Loan Rate: " << 100 * rate << "%\n";
restore(initialState, prec);
}
// redefine how Withdraw() works
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);
}
format setFormat()
{
// set up ###.## format
return cout.setf(std::ios_base::fixed,
std::ios_base::floatfield);
}
void restore(format f, precis p)
{
cout.setf(f, std::ios_base::floatfield);
cout.precision(p);
}
virtual void Withdraw(double amt);
double Balance() const;
virtual void ViewAcct() const;
定义虚函数,使用虚函数后程序将根据引用或者指针指向的对象类型来选择方法。
原则上在派生类方法中是通解析运算符来调用基类方法。
虚函数的工作原理
编译器处理虚函数的方法是:给每个对象添加一个隐藏成员。隐藏成员保存了一个指向函数地址数组的指针。这些数组称为虚函数表。
总之使用虚函数时内存和执行速度有一定的成本:
(1)每个对象都将增大,增大量为存储地址的空间。
(2)对于每个类编译器都将创建一个虚函数的地址表。 、
(3)每个函数调用,都需要执行一项额外的操作,即到表中查找地址。
以上说明构造函数不是虚函数,而析构函数是虚函数。
继承和动态内存分配
基类使用了动态分配,并重新定义赋值和复制构造函数,将怎样影响派生类呢?
第一种情况:
派生类不使用new
派生类不需要 定义显式 析构函数、复制构造函数、赋值构造函数。
第二种情况:
派生类使用new
这种情况下,必须为派生类定义显示析构函数 ,复制构造函数、赋值运算符。
使用动态内存分配和友元继承的示例:
// dma.h -- inheritance and dynamic memory allocation
#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);
};
// derived 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;
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);
};
// dma.cpp --dma class methods
#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];
std::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) // invoke base class copy constructor
{
style = new char[std::strlen(hs.style) + 1];
std::strcpy(style, hs.style);
}
hasDMA::~hasDMA()
{
delete [] style;
}
hasDMA & hasDMA::operator=(const hasDMA & hs)
{
if (this == &hs)
return *this;
baseDMA::operator=(hs); // copy base portion
delete [] style; // prepare for new style
style = new char[std::strlen(hs.style) + 1];
std::strcpy(style, hs.style);
return *this;
}
std::ostream & operator<<(std::ostream & os, const hasDMA & hs)
{
os << (const baseDMA &) hs;
os << "Style: " << hs.style << std::endl;
return os;
}
// dma.cpp --dma class methods
#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];
std::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) // invoke base class copy constructor
{
style = new char[std::strlen(hs.style) + 1];
std::strcpy(style, hs.style);
}
hasDMA::~hasDMA()
{
delete [] style;
}
hasDMA & hasDMA::operator=(const hasDMA & hs)
{
if (this == &hs)
return *this;
baseDMA::operator=(hs); // copy base portion
delete [] style; // prepare for new style
style = new char[std::strlen(hs.style) + 1];
std::strcpy(style, hs.style);
return *this;
}
std::ostream & operator<<(std::ostream & os, const hasDMA & hs)
{
os << (const baseDMA &) hs;
os << "Style: " << hs.style << std::endl;
return os;
}
// 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 << "Displaying 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;
// std::cin.get();
return 0;
}