第13章——类继承

 

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<<().

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值