essential c++读书笔记5

第五章 面向对象编程风格

5.1 面向对象编程

继承:将一群相关的类组织起来,并让我们得以分享期间的共通数据和操作行为; 父类:基类; 子类:派生类。
多态:可以使我们操纵不同类时,如同操纵单一个体。 让基类的pointer 和 reference 得以十分透明的指向任何一个派生类的对象。
动态绑定:解析操作会延迟到运行时才进行。
静态绑定:程序执行之前就已经解析出应该调用哪一个函数。
虚函数:virtual 实现了多态的机制。基类定义虚函数,子类可以重写该函数;在派生类中对基类定义的虚函数进行重写时,需要在派生类中声明该方法为虚方法。
当子类重新定义了父类的虚函数后,当父类的指针指向子类对象的地址时,[即B b; A a = &b;] 父类指针根据赋给它的不同子类指针,动态的调用子类的该函数,而不是父类的函数,
且这样的函数调用发生在运行阶段,而不是发生在编译阶段,称为动态联编。而函数的重载可以认为是多态,只不过是静态的。

同时在函数构造时,会依次调用父类、子类、子子类的调用函数;析构的顺序则相反。 使用虚函数后,子类实现父类的虚函数后,调用该函数就会调用子类的函数而不是父类的函数

5.3 不带继承的多态

所有数列类型名称被放在一个名为ns_type的枚举类型中

class num_sequence{
public: 
   enum ns_type{
       ns_unset,ns_fi,ns_pell,ns_lucas,ns_tri,ns_squ,ns_pen
   };
}

nstype() 函数会检测整数参数是否返回某一有效数列。

class num_sequence{
public:
    
    static ns_type nstype(int num)
    {
        return num <= 0 || num >= num_seq ? ns_unset : static_cat< ns_type > (num);
    }
};

static_cat是个特殊转换记号,可将num转换为对应的ns_type

不带继承的多态的缺点:费工夫,且事后维护工程浩大

5.4 定义一个抽象基类

定义抽象类的第一个步骤:找出所有子类共通操作行为。就是定义基类的公有接口。

class num_sequence{
public:
    int elem(int pos);
    void gen_elems(int pos);
    const char* what_am_i() const;
    ostream& print(ostream &os = cout) count;
    bool check_integrity(int pos);
    static int max_elems();
}

第二步:找出哪些操作行为与类型相关,也就是说有哪些行为必须根据不同的派生类而有不同的实现方式。这些操作行为应该成为类继承体系中的虚函数。

class num_sequence{
public:
    int elem(int pos);                                  
    void gen_elems(int pos);                          //产生数列元素
    const char* what_am_i() const;		      //不确定是否有关 与类型
    ostream& print(ostream &os = cout) count;
    bool check_integrity(int pos);                    //检测是否有效
    static int max_elems();
}

第三步:找出每个操作行为的访问层次。皆可访问:public 除基类外不需要用到:private 可以被派生类用到,但不允许一般程序使用

class num_sequence{
public:
    virtual ~num_sequence(){};
    virtual int elem(int pos)const = 0;                                  
    virtual const char* what_am_i() const = 0;		      //不确定是否有关 与类型
    virtual ostream& print(ostream &os = cout) count =0;
   
    static int max_elems() {retun _max_elems;};

protected:
    virtual void gen_elems(int pos) const =0;
     bool check_integrity(int pos);                    //检测是否有效
     const static int _max_elems = 1024;
}

每个虚函数,要么得有定义,要么设置为纯虚函数。
任何类如果声明一个或多个纯虚函数,那么由于其接口的不完整性(纯虚函数没有函数定义),程序无法为其产生任何对象。这种类只能作为派生类的子对象使用,而且前提是这些派生类必须
为所有虚函数提供确切的定义。
为什么要定义纯虚函数?
纯虚函数是为你的程序制定一种标准,即只要你继承了我,就必须按照我的标准来,实现我所有的方法,否则你也是虚拟的。

根据规则,凡基类定义有一个或读多个虚函数,应该要将其destructor声明为virtual。

class num_sequence{
public:
    virtual ~num_sequence();
};

5.5 定义一个派生类

派生类由两部分组成:一是基类构成的子对象,由基类的non-static data member组成;二是派生类的部分。

派生类的名称之后紧跟着冒号,关键字public,及基类的名称。

#include "num_sequence.h"
class Fibonacci : public num_sequence{
public:
    // ....
}

Fibonacci必须为从基类继承而来的每个纯虚函数提供对应的实现,还必须声明专属的member。

派生类的虚函数必须精确吻合基类中的函数原型,在类之外对虚函数进行定义时,不必指明关键字virtual。

每当派生类有某个member与其基类的member同名,便会遮掩注基类的那份member。也就是说,派生类内对该名称的任何使用,都会被解析为该派生类自身的那份member,而非继承来的那份member。
该问题的产生是由于基类中该函数 被视为虚函数。

5.7 基类应该多么抽象

Data member如果是个reference,必须在constructor的member initialize list中加以初始化。一旦初始化,就再也无法指向另一个对象。如果data member是pointer,就可以在construction内加以初始化,
也可以将它初始化为null,稍后再令它指向某个有效的内存地址。

5.8 初始化、析构、复制

初始化操作可以留给每个派生类,但这么做会有潜在的危机。
较好的设计方式是,为基类提供constructor,并利用这个constructor处理基类所声明的所有data member的初始化操作。

5.9 在派生类中定义一个虚函数

如果我们继承了纯虚函数,那么这个派生类也会被视为抽象类,也就无法为它定义任何对象。
为什么要定义纯虚函数?
纯虚函数是为你的程序制定一种标准,即只要你继承了我,就必须按照我的标准来,实现我所有的方法,否则你也是虚拟的。

如果我们决定覆盖基类所提供的虚函数,那么派生类提供的新定义,其函数原型必须完全符合基类所声明的函数原型,包括:参数列表、返回类型、常量性(const-ness)。

“返回类型必须完全吻合”规则也有意外 – 当基类的虚函数返回某个基类形式时,

class num_sequence{
public:
    //派生类的clone()可返回一个指针,
    //指向num_sequence的任何一个派生类
    virtual num_sequence *clone() =0;
    //....
}

派生类中的同名函数便可以返回该基类所派生出来的类型

class Fibonacci : public num_sequence{
public:
    //Fibonacci派生自num_sequence
    //在派生类中,关键字virtual并非必要
    Fibonacci *clone(){return new Fibonacci(*this);}
}

5.10 运行时的类型鉴定机制

怎么合理设计一份 what_am_i()函数,使其实现其功能?
1、在每个子类都拥有一份what_am_i()函数,都返回一个足以代表该类的字符串;
2、只在父类提供一份what_am_i()函数,令各派生类通过继承机制加以复用。
3、为num_sequence增加一个string member,并令每一个派生类的constructor都将自己的类名作为参数,传给num_sequence的constructor。
4、利用typeid运算符。运行时类型鉴定机制的一部分,由程序语言支持。

#include <typeinfo>

inline const char* num_sequence::what_am_i() const {return typeid(*this).name();}

ps -> gen_elems(64);

预期调用的是Fibonacci的gen_elems。但编译时却会发生错误。
为了调用Fibonacci所定义的gen_elems(),必须指示编译器,将ps的类型转换为Fibonacci指针。static_cast 运算符可以担起这项任务。

if(typeid (*ps) == typeid(Fibonacci))
{
    Fibonacci *pf = static_cast<Fibonacci*> (ps);   //无条件转换
    pf->gen_elems(64);
}

static_cast 也有潜在危险,因为编译器无法确认我们所进行的转换操作是否完全正确。

dynamic_cast 运算符就可以提供有条件的转换:

if(Fibonacci *pf = dynamic_cast<Fibonacci*> (ps))
    pf->gen_e;ems(64);
### 回答1: 《Essential C》是一本C语言入门教材,它每一章节都设计了实例来帮助读者理解概念和语言基础。电子版则更方便了读者的阅读和学习。 电子版提供了搜索和书签功能,读者可以根据自己的需求快速找到需要的内容并添加书签方便以后查阅。此外,电子版还可以进行自定义设置,例如字体大小、背景颜色等,可以适应个人的视力和阅读习惯。 另外,与传统纸质书同,电子版可以在同设备上进行阅读,无论是手机、平板还是电脑等,都可以随时随地进行学习。在线阅读还能通过云端同步功能实现多设备共享,无需反复购买纸质版或电子版。 总之,《Essential C》电子版具有方便、灵活、多样化的优点,可以提高学习效率和便利性,是入门C语言学习者的好帮手。 ### 回答2: Essential C是一本C语言的经典教材,在C语言学习领域有着重要的地位。它浅显易懂,通俗易懂的文字风格以及丰富的示例代码,为初学者提供了优秀的学习资源。 Essential C电子版的出现,更加方便了广大编程爱好者的学习。电子版易于获取、拷贝和搜索,而且免费提供下载,无需支付任何费用。同时,它还能在同的设备上进行阅读,让学习变得更加灵活。 电子版还具有与纸质版同的特点。它可以轻松地添加笔记、书签等功能,方便读者随时回忆所学习的知识点。此外,电子版还提供了灵活的屏幕阅读方式,通过缩放和改变字体大小、背景色等方式来使读者更加舒适的阅读。 过,电子版也存在一些缺点。因为它需要电子设备才能进行阅读,所以它的可移动性和泛用性相较于实体书籍较低。此外,对于想要离线阅读的读者,电子版便具有实体书籍的优势。 综上所述,Essential C电子版为学习C语言的初学者提供了更加方便、灵活的学习方式,但它并能完全替代实体书籍,两者都有自己的优缺点,而最终选择哪一种方式取决于读者自身的需求和使用习惯。 ### 回答3: Essential C是一本C语言程序设计的入门级教材,在学习C语言的过程中,是十分实用的一本教材。其电子版更是让读者们便于随时随地进行阅读学习,更加方便。 Essential C的电子版为读者来了许多便利,首先在阅读上,受到纸质版阅读的时间和地点的限制,可以通过任何一台电子设备,如电脑,手机,平板等就能轻松阅读。其次,在使用上,电子版更加方便了读者的查找和学习体验,例如增加了搜索功能,让读者更加便捷地找到所需要的内容,并且还能够进行注释和标记,极大提高了学习效率。 同时,Essential C的电子版还可以对其内容进行快速更新升级,保证其内容始终和时代同步,并且在学习之外,电子版还可进行交互式教学。比如说,在阅读的同时可以进行代码编辑等操作,相比于传统的纸质版,更具有互动性和趣味性。 总之,Essential C的电子版在方便性、使用性、更新性、互动性方面都具有很大优势,更加适合现代读者的学习习惯,极大地促进了C语言的教学工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值