c++ primer阅读笔记-15章-5

15.5. 继承情况下的类作用域

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

15.5.1. 名字查找在编译时发生

1、对象、引用或指针的静态类型(???)决定了对象能够完成的行为。甚至当静态类型和动态类型可能不同的时候,就像使用基类类型的引用或指针时可能会发生的,静态类型仍然决定着可以使用什么成员。例如,可以给 Disc_item 类增加一个成员,该成员返回一个保存最小(或最大)数量和折扣价格的 pair 对象:

   class Disc_item : public Item_base {
     public:
         std::pair<size_t, double> discount_policy() const
             { return std::make_pair(quantity, discount); }
         // other members as before
     };
只能通过 Disc_item 类型或 Disc_item 派生类型的对象、指针或引用访问 discount_policy:

   Bulk_item bulk;
     Bulk_item *bulkP = &bulk;  // ok: static and dynamic types are the same
     Item_base *itemP = &bulk;  // ok: static and dynamic types differ
     bulkP->discount_policy();  // ok: bulkP has type Bulk_item*
     itemP->discount_policy();  // error: itemP has type Item_base*

重新定义 itemP 的访问是错误的,因为基类类型的指针(引用或对象)只能访问对象的基类部分,而在基类中没有定义 discount_policy 成员。

15.5.2. 名字冲突与继承

1、与基类成员同名的派生类成员将屏蔽对基类成员的直接访问。

2、可以使用作用域操作符访问被屏蔽的基类成员。

15.5.3. 作用域与成员函数

1、在基类和派生类中使用同一名字的成员函数,其行为与数据成员一样:在派生类作用域中派生类成员将屏蔽基类成员。即使函数原型不同,基类成员也会被屏蔽

2、局部作用域中声明的函数不会重载全局作用域中定义的函数(第 7.8.1 节),同样,派生类中定义的函数也不重载基类中定义的成员。通过派生类对象调用函数时,实参必须与派生类中定义的版本相匹配,只有在派生类根本没有定义该函数时,才考虑基类函数。

重载函数

3、像其他任意函数一样,成员函数(无论虚还是非虚)也可以重载。派生类可以重定义所继承的 0 个或多个版本。

4、如果派生类重定义了重载成员,则通过派生类型只能访问派生类中重定义的那些成员。

5、如果派生类想通过自身类型使用的重载版本,则派生类必须要么重定义所有重载版本,要么一个也不重定义。


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

15.5.4. 虚函数与作用域

1、要获得动态绑定,必须通过基类的引用或指针调用虚成员。当我们这样做时,编译器器将在基类中查找函数。假定找到了名字,编译器就检查实参是否与形参匹配。

2、现在可以理解虚函数为什么必须在基类和派生类中拥有同一原型了。如果基类成员与派生类成员接受的实参不同,就没有办法通过基类类型的引用或指针调用派生类函数。考虑如下(人为的)为集合:

 class Base {
     public:
         virtual int fcn();
     };
     class D1 : public Base {
     public:
          // hides fcn in the base; this fcn is not virtual
          int fcn(int); // parameter list differs from fcn in Base
          // D1 inherits definition of Base::fcn()
     };
     class D2 : public D1 {
     public:
         int fcn(int); // nonvirtual function hides D1::fcn(int)
         int fcn();    // redefines virtual fcn from Base
     };
D1 中的 fcn 版本没有重定义 Base 的虚函数 fcn,相反,它屏蔽了基类的 fcn。结果 D1 有两个名为 fcn 的函数:类从 Base 继承了一个名为 fcn 的虚函数,类又定义了自己的名为 fcn 的非虚成员函数,该函数接受一个 int 形参。但是,从 Base 继承的虚函数不能通过 D1 对象(或 D1 的引用或指针)调用,因为该函数被 fcn(int) 的定义屏蔽了。
类 D2 重定义了它继承的两个函数,它重定义了 Base 中定义的 fcn 的原始版本并重定义了 D1 中定义的非虚版本。

通过基类调用被屏蔽的虚函数

3、通过基类类型的引用或指针调用函数时,编译器将在基类中查找该函数而忽略派生类:

  Base bobj;  D1 d1obj;  D2 d2obj;
     Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;
     bp1->fcn();   // ok: virtual call, will call Base::fcnat run time
     bp2->fcn();   // ok: virtual call, will call Base::fcnat run time
     bp3->fcn();   // ok: virtual call, will call D2::fcnat run time

三个指针都是基类类型的指针,因此通过在 Base 中查找 fcn 来确定这三个调用,所以这些调用是合法的。另外,因为 fcn 是虚函数,所以编译器会生成代码,在运行时基于引用指针所绑定的对象的实际类型进行调用。在 bp2 的情况,基本对象是 D1 类的,D1 类没有重定义不接受实参的虚函数版本,通过 bp2 的函数调用(在运行时)调用 Base 中定义的版本。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值