C++ primer 第五版个人笔记 第十五章 面向对象程序设计

15.1 OOP概述

  1. 派生类通过使用类派生列表明确指出它是从哪一个(些)基类继承而来的;形式是,首先是一个冒号,后面紧跟以逗号分隔的基类列表,其中每个基类前面可以有访问说明符;
    class Bulk_quote: public Quote, protect Quote1
    {
    }

     

  2. 派生类必须在其内部对所有重新定义的虚函数进行声明,派生类可以在这样的函数前加上virtual关键字,但不是必须的, C++11新标准允许派生类显式地著名它将使用哪个成员函数来改写基类的虚函数,具体措施是在该函数的形参列表后增加一个override关键字;

  3. 动态绑定有时称为运行时绑定,当使用基类的引用(或指针)调用一个虚函数时将发生动态绑定,案例见527页;

15.2 定义基类和派生类

  1.  作为继承关系中根节点的类(根基类)通常都会定义一个虚析构函数,即使该函数不执行任何实际操作也是如此;
  2. 基类必须将它的两种成员函数区分开来,一种是基类希望派生类进行覆盖的函数,通常声明为虚函数(virtual),一种是希望派生类直接继承不做任何改变的函数;当使用指针或引用调用虚函数时,该调用将被动态绑定;
  3. 任何构造函数之外的非静态函数都可以是虚函数,关键字virtual只能出现在类内部的声明语句之前而不能用于类外部的函数定义
  4. 成员函数如果没有被声明为虚函数,则其解析过程发生在编译时而非运行时;
  5. 派生类经常覆盖继承的虚函数,如果没有覆盖,则直接继承基类的版本;
  6. 编译器会隐式地执行派生类到基类的类型转换,当使用基类指针或引用来调用派生类的基类成员部分,但反过来基类不能隐式地转化为派生类;
  7. 派生类初始化的顺序,先初始化基类的部分,然后按照声明的顺序依次初始化派生类的成员;
  8. 派生类的作用域嵌套在基类的作用域之内,对于派生类的成员来说,它使用派生类成员的方式与使用基类成员的方式没什么不同;
  9. 派生类的声明要包含类名但是不包含它的派生列表;
  10. C++11新标准提供了一种防止继承发生的方法,在类名后跟一个关键字final,表示此类不可继承
    class noDerived final {/* */ };

     

  11.  练习15.7

    class limitedQuote : public Quote
    {
    private:
    	size_t max_qty;
    	double discount;
    public:
    	limitedQuote(string&book, double sales_price, int qty, double disc):
    			Quote(book, sales_price), max_qty(qty), discount(disc){}
    	double net_price(size_t n) const override
    	{
    		if (n > max_qty)
    		{
    			return (n - max_qty) * price + max_qty * price * (1 - discount);
    		}
    		else
    		{
    			return n * price * (1 - discount);
    		}
    	}
    };

     

  12. 基类的指针或引用的静态类型可能与其动态类型不一致,主要看其绑定的对象类型是基类类型还是派生类类型;

  13.  当用一个派生类对象给一个基类对象初始化或赋值时,只有该派生类对象中的基类部分会被拷贝、移动或赋值,它的派生类部分将被忽略掉;

  14. 理解具有继承关系的类之间发生的类型转换,有三点至关重要 :

    1.从派生类向基类的类型转换只对指针或引用类型有效(即派生类的指针可以转换为基类指针)
    2.基类向派生类不存在隐式类型转换
    3.派生类向基类的类型转换可能会由于访问受限而变得不可行

     

15.3 虚函数

  1. 必须为每一个虚函数都提供定义而不管它是否被用到了,因为编译器也无法确定到底会用哪个虚函数;
  2. 多态(polymorphism)这个词源于希腊语,含义是“多种形式”,我们把具有继承关系的多个类型称为多态类型;引用或指针的静态类型与动态类型不同这一事实正是C++语言支持多态性的根本所在;
  3. 对非虚函数的调用在编译时进行绑定,通过对象进行的函数(无论是不是虚函数)调用在编译时绑定,可以称为静态绑定,通过指针对虚函数的调用称为动态绑定,仅由动态绑定时对象的动态类型才有可能与静态类型不同;
  4. 一个派生类的函数如果覆盖了某个继承而来的虚函数,则它的形参类型必须与它覆盖的基类函数完全一致;
  5. 如果虚函数使用默认实参,则基类和派生类中定义的默认实参最好一致,因为使用哪个默认实参由指针的类型决定;
  6. 回避虚函数的机制是使用作用域运算符:: 强调使用基类或者某个派生类的虚函数,如
    double undiscounted = baseP->Quote::net_price(42);
    //baseP为基类指针,Quote::显式告知使用Quote版本的net_price()虚函数
     

15.4 抽象基类

  1. 在函数体的位置(声明语句的分号前)书写=0就可以将一个虚函数说明为纯虚函数,其中,=0只能出现在类内部的虚函数声明语句处;我们也可以为纯虚函数提供定义,不过函数体必须在类的外部;
  2. 含有纯虚函数的类是抽象基类(abstract base class)。抽象基类负责定义接口,后续的其他类可以覆盖该接口,而且我们不能(直接)创建一个抽象基类的对象;抽象基类的派生类如果没有给纯虚函数定义仍然是抽象基类;
  3. 练习15.17 报错内容“E0322    不允许使用抽象类类型 "Disc_Quote" 的对象:  “

 

15.5 访问控制与继承

  1. 派生类的成员和友元只能访问派生类对象中的基类部分的受保护成员,对于普通的基类对象中的保护成员不具有特殊的访问权限;
  2. 派生访问说明符对于派生类的成员(及友元)能否访问其直接基类的成员没什么影响,对基类成员的访问权限只与基类中的访问说明符有关;
  3. 派生访问说明符的目的是控制派生类用户(派生类的对象及派生类的派生类)对于基类成员的访问权限;详细看544页例;
  4. 类成员的访问说明
    继承方式\原访问说明符 public protect private
    public public protect private
    protect protect protect private
    private private private private

     

  5. 友元关系不能传递也不能继承,每个类负责控制各自成员的访问权限;

  6. using可以改变个别成员的可访问性,但是只能为派生类自己能访问的名字提供声明,详见546页;

  7. struct关键字和class关键字定义的类的区别仅仅是默认成员访问说明符及默认派生访问说明符,struct默认public访问,class默认private访问;

  8. 派生类向基类转换的可访问性3个原则详见544页,结合练习15.18与15.19使用效果更佳;

15.6 继承中的类作用域

  1. 派生类的成员将隐藏同名的基类成员,如果希望使用隐藏的成员,可以使用作用域运算符::;
  2. 声明在内层作用域的函数并不会重载声明在外层作用域的函数,如果派生类的成员与基类的某个成员同名,派生类将隐藏基类成员,即使形参列表不一致,基类成员也会被隐藏而不是重载;这里重载和隐藏的区别在于如果使用对应基类形参的函数,隐藏会报错,而重载会匹配;
  3. 如果基类中有同名的重载函数,那么派生类的同名函数将覆盖基类中重载函数的0个或多个实例,有时候只需要覆盖基类重载集合中的部分函数,一个简单的方法是使用using 声明语句;

15.7 构造函数与拷贝控制

  1. 对于有继承关系的基类,我们应该把它的析构函数定义为虚析构函数,否则delete一个指向派生类对象的基类指针将产生未定义的行为;
  2. 如果一个类定义了析构函数,即使它通过=default的形式使用了合成的版本,编译器也不会为这个类合成移动操作;
  3. 当派生类定义了拷贝或移动操作时,该操作负责拷贝或移动包括基类部分成员在内的整个对象,而析构函数只负责销毁派生类自己分配的资源,派生类的基类部分的资源将由基类的析构函数部分隐式销毁;
  4. 对象销毁的顺序与构造的顺序相反,派生类析构函数先执行,然后是基类的析构函数;
  5. 如果构造函数或析构函数调用了某个虚函数,则我们应该执行与构造函数或析构函数所属类型相对应的虚函数版本;
  6. 派生类不能继承基类的默认构造函数,,方式是提供一条注明了基类名的using声明语句,举例见557页,一个构造函数的using声明不会改变该构造函数的访问级别(普通成员会改变),而且一个using声明语句不能指定explicit或constexpr;

15.8 容器与继承

  1. 当派生类对象被赋值给基类对象时,其中的 派生类部分将被“切掉”,因此容器和存在继承关系的类型无法兼容;
  2. 当我们希望在容器中存放具有继承关系的对象时,我们实际上存放的通常是基类的指针(更好的选择是智能指针);

15.9 文本查询程序再探

自己实现的程序贴在下面

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章