C++学习笔记十五-面向对象编程(二)

一、继承情况下的类作用域

     1.每个类都保持着自己的作用域,在该作用域中定义了成员的名字。在继承情况下,派生类的作用域嵌套在基类作用域中。如果不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义。正是这种类作用域的层次嵌套使我们能够直接访问基类的成员,就好象这些成员是派生类成员一样。

 

     2.名字查找在编译时发生:对象、引用或指针的静态类型决定了对象能够完成的行为。甚至当静态类型和动态类型可能不同的时候,就像使用基类类型的引用或指针时可能会发生的,静态类型仍然决定着可以使用什么成员。基类类型的指针(引用或对象)只能访问对象的基类部分。

 

     3.名字冲突与继承:与基类成员同名的派生类成员将屏蔽对基类成员的直接访问使用作用域操作符访问被屏蔽成员。设计派生类时,只要可能,最好避免与基类成员的名字冲突。

     4.作用域与成员函数:在基类和派生类中使用同一名字的成员函数,其行为与数据成员一样:在派生类作用域中派生类成员将屏蔽基类成员。即使函数原型不同,基类成员也会被屏蔽。派生类中定义的函数也不重载基类中定义的成员。通过派生类对象调用函数时,实参必须与派生类中定义的版本相匹配,只有在派生类根本没有定义该函数时,才考虑基类函数。

     5.重载函数:

       a.像其他任意函数一样,成员函数(无论虚还是非虚)也可以重载。派生类可以重定义所继承的 0 个或多个版本。如果派生类重定义了重载成员,则通过派生类型只能访问派生类中重定义的那些成员。如果派生类想通过自身类型使用的重载版本,则派生类必须要么重定义所有重载版本,要么一个也不重定义

       b.派生类不用重定义所继承的每一个基类版本,它可以为重载成员提供 using 声明,一个 using 声明只能指定一个名字,不能指定形参表,因此,为基类成员函数名称而作的 using 声明将该函数的所有重载实例加到派生类的作用域。将所有名字加入作用域之后,派生类只需要重定义本类型确实必须定义的那些函数,对其他版本可以使用继承的定义。

 

     6.虚函数与作用域:要获得动态绑定,必须通过基类的引用或指针调用虚成员。当我们这样做时,编译器器将在基类中查找函数。假定找到了名字,编译器就检查实参是否与形参匹配。所以虚函数必须在基类和派生类中拥有同一原型了。

     7.通过基类调用被屏蔽的虚函数:通过基类类型的引用或指针调用函数时,编译器将在基类中查找该函数而忽略派生类

     8.总结:名字查找与继承

       理解 C++ 中继承层次的关键在于理解如何确定函数调用。确定函数调用遵循以下四个步骤:

           1.首先确定进行函数调用的对象、引用或指针的静态类型。

           2.在该类中查找函数,如果找不到,就在直接基类中查找,如此循着类的继承链往上找,直到找到该函数或者查找完最后一个类。如果不能在类或其相关基类中找到该名字,则调用是错误的。

           3.一旦找到了该名字,就进行常规类型检查,查看如果给定找到的定义,该函数调用是否合法。

           4.假定函数调用合法,编译器就生成代码。如果函数是虚函数且通过引用或指针调用,则编译器生成代码以确定根据对象的动态类型运行哪个函数版本,否则,编译器生成代码直接调用函数。

 

二、纯虚函数:类的头文件中在函数形参表末尾 =0 声明的虚函数。类不必(但可以)定义纯虚函数。带纯虚函数的类为抽象类。如果派生类没有定义所继承的纯虚函数的自身版本,则派生类也是抽象类。

   在函数形参表后面写上 = 0 以指定纯虚函数:

     class Disc_item : public Item_base {
     public:
         double net_price(std::size_t) const = 0;
     };
  1.将函数定义为纯虚能够说明,该函数为后代类型提供了可以覆盖的接口,但是这个类中的版本决不会调用。
  2.试图创建抽象基类的对象将发生编译时错误
  3.含有(或继承)一个或多个纯虚函数的类是抽象基类。除了作为抽象基类的派生类的对象的组成部分,不能创建抽象类型的对象。
三、容器与继承:我们希望使用容器(或内置数组)保存因继承而相关联的对象。但是,对象不是多态的,这一事实对将容器用于继承层次中的类型有影响。
   1.如果定义 multiset 保存基类类型的对象:
     multiset<Item_base> basket;
     Item_base base;
     Bulk_item bulk;
     basket.insert(base);  // ok: add copy of base to basket
     basket.insert(bulk);  // ok: but bulk sliced down to its base part
    则加入派生类型的对象时,只将对象的基类部分保存在容器中。记住,将派生类对象复制到基类对象时,派生类对象将被切掉.

       因为派生类对象在赋值给基类对象时会被“切掉”,所以容器与通过继承相关的类型不能很好地融合。

 

四、句柄类与继承:

        C++ 中一个通用的技术是定义包装(cover)类或句柄类。句柄类存储和管理基类指针。指针所指对象的类型可以变化,它既可以指向基类类型对象又可以指向派生类型对象。用户通过句柄类访问继承层次的操作。因为句柄类使用指针执行操作,虚成员的行为将在运行时根据句柄实际绑定的对象的类型而变化。因此,句柄的用户可以获得动态行为但无须操心指针的管理。

posted @ 2012-07-07 16:00  ForFreeDom  阅读(423)  评论(0编辑  收藏  举报