l 派生一个类
eg:
class RatedPlayer:public TableTennisPlayer{…};
派生类不能直接访问基类的私有成员,而必须通过基类的方法进行访问。
创建派生类对象时,程序首先创建基类对象。C++使用成员初始化列表句法来完成这种工作。
RatedPlayer::RatedPlayer(unsigned int r,const char *fn,const char *ln,bool ht)
:TableTennisPlayer(fn,ln,ht)
{rating = r;}
除非要使用默认构造函数,否则应显式调用正确的基类构造函数。
l 派生类和基类之间的特殊关系
基类指针可以在不进行显式类型转换的情况下指向派生类对象;基类引用可以在不进行显式类型转换的情况下引用派生类对象。
RatedPlayer rplayer1(1140,”Mallory”,”Duck”,true);
TableTennisPlayer &rt=rplayer;
TableTennisPlayer *pt=&rplayer;
rt.Name();
pt->Name();
通常,C++要求引用和指针类型与赋给的类型匹配,但这一规则对继承来说是例外。不过,这种例外只是单向的,不可以将对象和地址赋给派生类的引用和指针。
l 关系
派生类和基类之间的特殊关系是基于C++继承的底层模型的。实际上,C++有3种继承方式:公有继承,保护继承和私有继承。公有继承是常用的方式,它建立一种is-a关系。
还有另外几种关系:has-a,is-like-a,is-implemented-a,uses-a.
推荐公有继承,建立is-a关系。
l 多态
使用虚方法可以使同一种方法在派生类和基类中的行为是不同的。
eg:
#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 ResetRate(double r) { rate = r; };
void ResetOwes() { owesBank = 0; }
};
#endif
程序根据对象类型来确定使用哪个版本的虚方法。方法在基类中被声明为虚拟的后,它在派生类中将自动成为虚方法。为基类声明一个虚拟析构函数是一种惯例。
ios_base::fmtflags initialState =
cout.setf(ios_base::fixed,ios_base::floatfield);
setf()方法返回一个值,这个值表示调用函数之前的格式状态。
为何需要虚拟析构函数
有这样一种情况:基类指针指向派生类对象。当删除这个基类指针时,如果析构函数不是虚拟的,则将调用对应于指针类型的析构函数,那么就不会调用派生类的析构函数当析构函数声明为虚拟后,则编译器会选择调用相应对象类型的析构函数。
l 访问控制
对于外部世界来说,保护成员的行为与私有成员相似,但对于派生类来说,保护成员的行为与公有成员相似。
l 抽象基类(ABC)
当类声明中包含纯虚函数时,则不能创建该类的对象。包含纯虚函数的类只用作基类。
纯虚函数声明的结尾处为=0。
纯虚函数的两个特点:
1. 可不提供函数定义
2. 使类成为抽象基类
l 继承和动态内存分配
当基类中使用new,派生类中没有使用new时,派生类不必显式定义析构函数,复制构造函数和赋值操作符。如果派生类中使用了new,则需要。
eg:
#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);
};
#endif
派生类析构函数自动调用基类析构函数。
baseDMA::~base()
{
delete [] label;
}
hasDMA::~hasDMA()
{
delete[] style;
}
复制构造函数
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::operator=(const hasDMA & hs)
{
if (this == &hs)
return *this;
baseDMA::operator=(hs); // copy base portion
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;
}
作为hasDMA类的友元,该函数能够访问style成员。由于hs进行了强制类型转换,所以能够访问基类的lable和rating成员。因为进行了转换,所以,显示用的是baseDMA类的友元函数operator<<().