第15章 面向对象编程

15.1、面向对象编程:概述

         在C++中,多态性仅用于通过继承而相关联的类型的引用或指针,通过基类的引用(或指针)调用虚函数时,发生动态绑定。

15.2、定义基类和派生类

15.2.3、派生类

         如class A :public C{};

         用作基类的类必须是已定义的(只是声明不行)。

15.2.4、virtual与其他成员函数

         非虚函数总是在编译时根据调用该函数的对象、引用或指针的类型而确定。

         派生类虚函数调用基类版本时,必须显式使用作用域操作符。如son->father::方法()。

如果一个调用省略了具有默认值的实参,则所有的值由调用该函数的类型定义,与对象的动态类型无关。即通过基类的引用或指针调用虚函数时,默认实参为在基类虚函数声明中指定的值,如果通过派生类的指针或引用调用虚函数,则默认实参是在派生类的版本中声明的值。

15.2.5、公有、私有和受保护的继承

         受保护继承:基类的public和protected成员派生成protected成员;私有继承,基类所有成员在派生类中全为private成员。

         使用private或protected派生的类不继承基类的接口,被称为实现继承。

         struct和class除了默认成员访问保护级别,没有其他区别。使用class保留字定义的派生类默认具有private继承,而struct保留字定义的类默认具有public继承。如可以struct A: public B{};其中B可以是类或结构体。

         派生类可以恢复继承成员的访问级别,但不能使访问级别比基类中原来指定的更严格或更宽松。通过用using A::成员名。成员名可以是函数名,如A中有size()成员函数,可以usingA::size;使得私有继承的A在子类中可以还原成A中size()的访问级别。

15.3、转换与继承

15.3.1、派生类到基类的转换

         将对象传给希望接受引用是的函数时,引用直接绑定到该对象,实参实际上是该对象的引用,对象本身未被复制,该对象仍是派生类型对象。将派生类对象传给希望接受基类类型对象(而不是引用)的函数时,该派生类对象的基类部分被复制到形参

         如果是public继承,则用户代码和后代类都可以使用派生类到基类的转换。如果类是使用private或protected继承派生的,则用户不能将派生类型对象转换为基类对象。对于后代类,如果是private则也不能转换,如果是protected继承,则可以。

         基类到派生类转换,则需要用显式转换,如static_cast(强制转换)、dynamic_cast(申请运行时进行检查)。

15.4、构造函数和复制控制

     构造函数初始化列表为类的基类和成员提供初始值,它并不指定初始化的执行次序。首先初始化基类,然后根据声明次序初始化派生类的成员。

     一个类只能初始化自己的直接基类。

     重构包括重新定义类层次,将操作和/或数据从一个类移到另一个类。

     构造函数只能初始化其直接基类的原因是每个类都定义了自己的接口,与该类的所有交互都应该通过该接口,即使对象是派生类对象的一部分也不例外。(初始化列表指定父类的父类构造函数是错误的)。

15.4.3、复制控制和继承

     派生类析构函数不负责撤销基类对象的成员,它只负责清除自己的成员。对象撤销顺序与构造顺序相反:首先运行派生类析构函数,然后按继承层次依次向上调用各基类析构函数。

15.4.4、虚析构函数

     基类析构函数是三法则(如果类需要析构函数,则类几乎也确实需要其他复制控制函数13.3节)的一个重要例外。如果基类为了将析构函数设为虚函数而具有空析构函数,那么,类具有析构函数并不表示也需要赋值操作符或复制构造函数。

     构造函数不能定义为虚函数,因为在构造函数运行时对象的动态类型还不完整。

15.4.5、构造函数和析构函数中的虚函数

     如果在构造函数或析构函数中调用虚函数,则运行的是为构造函数或析构函数自身类型定义的版本。(因为构造和析构时,类对象不完整)

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

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

     基类类型的指针(引用或对象)只能访问对象的基类部分。(多态前提是基类中有派生类的方法(虚函数))。

15.5.2、名字冲突与继承

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

15.5.3、作用域与成员函数

     在基类和派生类中使用同一名字的成员函数,会屏蔽基类成员,即使函数原型不同(如f()和f(int))也会屏蔽。

     如调用f(),在派生类中,一旦找到函数名(f(int)),编译器就不再继续查找了(不会去基类中找),调用方法定义不匹配,则出错。

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

如:class Base{public: virtual int f();};

class D1 : public Base{public: intf(int);};

class D2 : public D1{public: int f(int);int f();};

将这三个类对象指针传递给Base指针,则D1的Base指针调用f()是Base的f(),而D2成功实现多态。

15.6、纯虚函数

  含有(或继承)一个或多个纯虚函数的类是抽象基类。除了作为抽象基类的派生类的对象的组成部分,不能创建抽象类型的对象。

15.7、容器与继承

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

  可以显示地将基类对象强制转换为派生类对象并将结果对象加入容器。

15.8、句柄类与继承

  句柄类存储和管理基类指针。

15.8.1、指针型句柄

  对于赋值操作符,将右操作数的使用计数加1并复制指针,并且首先必须将左操作数的使用计数减1,如果使用计数减至0就删除指针。(因此这边特别要注意自身赋值,可以先将右操作数的使用计数加1)。

你可能感兴趣的:(C++primer,c++,对象,面向对象编程)