[C/C++] 第15章:面向对象编程 《 C++ Primer 》
第15章:面向对象编程
——面向对象编程基于三个基本概念:数据抽象,继承,动态绑定。
——用类进行数据抽象
——用类派生从一个类继承另一个类:派生类继承基类成员;
——动态绑定使编译器能够在运行时决定是使用基类中定义的函数还是派生类中定义的函数。
@学习摘录162:继承与动态绑定的作用:
——能够容易地定义与其他类相似但又不相同的新类,能够更容易地编写忽略这些相似类型之间区别的程序。
@学习摘录163:多态性
——面向对象编程的关键思想是多态性(polymorphism)
——继承而相关联的类型为多态类型。
@学习摘录164:继承
——派生类(derived class)能够继承基类(base class)定义的成员
——派生类可以无须改变而使用那些与派生类型具体特性不相关的操作
——派生类可以重定义那些与派生类型相关的成员函数,将函数特化,考虑派生类型的特性。Ok ! 特化。。
——在C++中,基类必须指出希望派生类重定义哪些函数,定义为virtual的函数是基类期待派生类重新定义的,基类希望派生类继承的函数不能定义为虚函数。
@学习摘录165:动态绑定(dynamic binding)
——我们能够编写程序使用继承层次中任意类型的对象,无须关心对象的具体类型。
@学习摘录166:protected成员
——可以认为protected访问标号是private和public的混合:
——1. 像private成员一样,protected成员不能被类的用户访问。
——2. 像public成员一样,protected成员可被该类的派生类访问。
——派生类只能通过派生类对象访问其基类的protected成员,派生类对其基类类型对象的protected成员没有特殊访问权限。 Good !
@学习摘录167:派生类类型接口
——简单地说:提供给派生类型接口是protected 成员和public成员的集合。 why?
@学习摘录168:派生类
——为了定义派生类,使用类派生列表(class derivation list)指定基类。
——class classname: access-label base-class
——这里的access-label 是public、protected或private, base-class 是已定义的类的名字。
@学习摘录169:派生类和虚函数 --- 一直为虚函数
——一旦函数在基类中声明为虚函数,它就“一直”为虚函数,派生类无法改变该函数为虚函数这一事实。
——派生类重定义虚函数时,可以使用virtual保留字,但不是必须这样做。
@学习摘录170:virtual与其他成员函数
——C++函数默认不使用动态绑定。
——要触发动态绑定,必须满足两个条件:
——第一,要将成员函数指定为虚函数。(默认的成员函数都是非虚函数)
——第二,要通过基类类型的引用或指针进行函数调用。
@学习摘录171:静态类型与动态类型的概念
——静态类型(static type)在编译时可知的引用类型或指针类型。
——动态类型(dynamic type)指针或引用所绑定的对象的类型,这是仅在运行时可知的。
@学习摘录172:C++中的多态性
——引用和指针的静态类型与动态类型可以不同,这是C++用以支持多态性的基石。
@学习摘录173:派生类函数调用基类版本
——只有成员函数中的代码才应该使用作用域操作符覆盖虚函数机制。
——覆盖虚函数机制,最常见的理由是为了派生类虚函数调用基类中的版本。
——派生类虚函数调用基类版本时,必须显式使用作用域操作符。如果派生类函数忽略了这样做,则函数调用会在运行时确定并且将是一个自身调用,从而导致无穷递归。why?
@学习摘录174:继承与组合
——定义一作为另一个类的公用派生类时,派生类应反映与基类的“是一种(Is A)”关系。
——在书店的例子中,基类表示按规定价格销售的书的概念,Bulk_item是一种书,但具有不同的定价策略。
——类型之间另一种常见的关系是称为“有一个(Has A)”的关系。
——书店的例子的类具有价格和ISBN.
@学习摘录175:友元关系与继承
——基类或派生类可以使其他类或函数成为友元。
——友元可以访问类的private和protected数据。
——友元关系不能继承。
@学习摘录176:转换与继承
——每个派生类对象包含一个基类部分。
——可以将派生类对象的引用转换为基类子对象的引用,对指针也类似。
——没有从基类引用(或基类指针)到派生类引用(或派生类指针)的(自动)转换。 哦!!
——构造函数和复制控制成员不能继承,每个类定义自己的构造函数和复制控制成员。
@学习摘录177:合成的派生类默认构造函数
——派生类的合成默认构造函数与非派生的构造函数只有一点不同:
——除了初始化派生类的数据成员外,还要初始化派生类对象的基类部分。
——基类部分由基类的默认构函数初始化。
@学习摘录178:派生类的构造次序
——构造函数初始化列表为类的基类和成员提供初始值,它并不指定初始化的执行次序。
——首先初始化基类,然后根据声明次序初始化派生类的成员。
@学习摘录179:只能初始化直接基类
——一个类只能初始化自己的直接基类。(直接基类就是在派生列表中指定的类。)
——如果类C从类B派生,类B从类A派生,那B是C的直接基类。
@学习摘录180:重构(refactioring)
——重构包括重新定义类层次,将操作和/或数据从一个类移到另一个类。
——为了适应应用程序的需要而重新设计类以便增加新函数或处理其他改变时,最有可能需要进行重构。
——然而,对类进行重构,或以任意其他方式改变类,使用这些类的任意代码都必须重新编译。
@学习摘录181:尊重基类接口
——构造函数只能初始化其直接基类的原因是每个类都定义了自己的接口。
——一旦定义了自己的接口,与该类对象的所有交互都应该通过接口。
@学习摘录182:定义派生类复制构造函数
——如果派生类显式定义自己的复制构造函数或赋值操作符,则该定义将完全覆盖默认定义。
——被继承类的复制构造函数和赋值操作符负责对基类成分以及类自己的成员进行复制或赋值。
@学习摘录183:派生类析构函数
——析构函数的工作与复制构函数和赋值操作符不同:派生类析构函数不负责撤销基类对象的成员。
——每个析构函数只负责清除自已的成员,对象的撤销顺序与构造顺序相反:首先运行派生类析构函数,然后按继承层次依次向上调用各基类析造函数。
——如果析构函数为虚函,那么通过指针调用时,运行哪个析构函数将因指针所指对象类型的不同而不同。
——即使析构函数没有工作要做,继承层次的根类也应该定义一个虚件构函数。
@学习摘录184:构造函数和赋值操作符不是虚函数
——在复制控制成员中,只有析构函数应定义为虚函数,构造函数不能定义为虚函数。
——在继承情况下,派生类的作用域嵌套在基类作用域中。如果不能在派生类作用域中确定名字,就在外围基类作用域查找该名字的定义。
@学习摘录185:名字冲突与继承
——与基类成员同名的派生类成员将屏蔽对基类成员的直接访问。
——可以使用作用域操作符访问被屏蔽的基类成员。
@学习摘录186:纯虚函数
——含有(或继承)一个或多个纯虚函数的类是抽象基类(abstract base class)。
——除了作为抽象基类的派生类的对象的组成部分,不能创建抽象类型的对象。
@学习摘录187:容器与继承
——因为派生类对象在赋值给基类对象时会被“切掉”,所以容器与通过继承相关的类型不能很好地融合。
第八节:句柄类与继承
——C++中面向对象编程中一个颇具讽刺意味的地方是,不能使用对象支持面对对象编程,相反,必须使用指针或引用。 Good ! 讽刺意味。。。
@学习摘录188:定义包装类和句柄类
——C++中一个通用的技术是定义包装(cover)类或句柄(handle)类。
——句柄类,存储和管理基类指针。
——指针所指对象的类型可以变化,它既可以指向基类类型对象又可以指向派生类型对象。
@学习摘录189:包装了继承层次的句柄有两个重要的设计考虑因素:
——1. 像对任何保存指针的类一样,必须确定对复制控制做些什么。
——2. 句柄类决定句柄接口屏蔽还是不屏蔽层次,如果不屏蔽层次,用户必须了解和使用基本层次中的对象。
——面向对象编程基于三个基本概念:数据抽象,继承,动态绑定。
——用类进行数据抽象
——用类派生从一个类继承另一个类:派生类继承基类成员;
——动态绑定使编译器能够在运行时决定是使用基类中定义的函数还是派生类中定义的函数。
@学习摘录162:继承与动态绑定的作用:
——能够容易地定义与其他类相似但又不相同的新类,能够更容易地编写忽略这些相似类型之间区别的程序。
@学习摘录163:多态性
——面向对象编程的关键思想是多态性(polymorphism)
——继承而相关联的类型为多态类型。
@学习摘录164:继承
——派生类(derived class)能够继承基类(base class)定义的成员
——派生类可以无须改变而使用那些与派生类型具体特性不相关的操作
——派生类可以重定义那些与派生类型相关的成员函数,将函数特化,考虑派生类型的特性。Ok ! 特化。。
——在C++中,基类必须指出希望派生类重定义哪些函数,定义为virtual的函数是基类期待派生类重新定义的,基类希望派生类继承的函数不能定义为虚函数。
@学习摘录165:动态绑定(dynamic binding)
——我们能够编写程序使用继承层次中任意类型的对象,无须关心对象的具体类型。
@学习摘录166:protected成员
——可以认为protected访问标号是private和public的混合:
——1. 像private成员一样,protected成员不能被类的用户访问。
——2. 像public成员一样,protected成员可被该类的派生类访问。
——派生类只能通过派生类对象访问其基类的protected成员,派生类对其基类类型对象的protected成员没有特殊访问权限。 Good !
@学习摘录167:派生类类型接口
——简单地说:提供给派生类型接口是protected 成员和public成员的集合。 why?
@学习摘录168:派生类
——为了定义派生类,使用类派生列表(class derivation list)指定基类。
——class classname: access-label base-class
——这里的access-label 是public、protected或private, base-class 是已定义的类的名字。
@学习摘录169:派生类和虚函数 --- 一直为虚函数
——一旦函数在基类中声明为虚函数,它就“一直”为虚函数,派生类无法改变该函数为虚函数这一事实。
——派生类重定义虚函数时,可以使用virtual保留字,但不是必须这样做。
@学习摘录170:virtual与其他成员函数
——C++函数默认不使用动态绑定。
——要触发动态绑定,必须满足两个条件:
——第一,要将成员函数指定为虚函数。(默认的成员函数都是非虚函数)
——第二,要通过基类类型的引用或指针进行函数调用。
@学习摘录171:静态类型与动态类型的概念
——静态类型(static type)在编译时可知的引用类型或指针类型。
——动态类型(dynamic type)指针或引用所绑定的对象的类型,这是仅在运行时可知的。
@学习摘录172:C++中的多态性
——引用和指针的静态类型与动态类型可以不同,这是C++用以支持多态性的基石。
@学习摘录173:派生类函数调用基类版本
——只有成员函数中的代码才应该使用作用域操作符覆盖虚函数机制。
——覆盖虚函数机制,最常见的理由是为了派生类虚函数调用基类中的版本。
——派生类虚函数调用基类版本时,必须显式使用作用域操作符。如果派生类函数忽略了这样做,则函数调用会在运行时确定并且将是一个自身调用,从而导致无穷递归。why?
@学习摘录174:继承与组合
——定义一作为另一个类的公用派生类时,派生类应反映与基类的“是一种(Is A)”关系。
——在书店的例子中,基类表示按规定价格销售的书的概念,Bulk_item是一种书,但具有不同的定价策略。
——类型之间另一种常见的关系是称为“有一个(Has A)”的关系。
——书店的例子的类具有价格和ISBN.
@学习摘录175:友元关系与继承
——基类或派生类可以使其他类或函数成为友元。
——友元可以访问类的private和protected数据。
——友元关系不能继承。
@学习摘录176:转换与继承
——每个派生类对象包含一个基类部分。
——可以将派生类对象的引用转换为基类子对象的引用,对指针也类似。
——没有从基类引用(或基类指针)到派生类引用(或派生类指针)的(自动)转换。 哦!!
——没有从派生类型对象到基类类型对象的直接转换。
第四节:构造函数和复制控制——构造函数和复制控制成员不能继承,每个类定义自己的构造函数和复制控制成员。
@学习摘录177:合成的派生类默认构造函数
——派生类的合成默认构造函数与非派生的构造函数只有一点不同:
——除了初始化派生类的数据成员外,还要初始化派生类对象的基类部分。
——基类部分由基类的默认构函数初始化。
@学习摘录178:派生类的构造次序
——构造函数初始化列表为类的基类和成员提供初始值,它并不指定初始化的执行次序。
——首先初始化基类,然后根据声明次序初始化派生类的成员。
@学习摘录179:只能初始化直接基类
——一个类只能初始化自己的直接基类。(直接基类就是在派生列表中指定的类。)
——如果类C从类B派生,类B从类A派生,那B是C的直接基类。
@学习摘录180:重构(refactioring)
——重构包括重新定义类层次,将操作和/或数据从一个类移到另一个类。
——为了适应应用程序的需要而重新设计类以便增加新函数或处理其他改变时,最有可能需要进行重构。
——然而,对类进行重构,或以任意其他方式改变类,使用这些类的任意代码都必须重新编译。
@学习摘录181:尊重基类接口
——构造函数只能初始化其直接基类的原因是每个类都定义了自己的接口。
——一旦定义了自己的接口,与该类对象的所有交互都应该通过接口。
@学习摘录182:定义派生类复制构造函数
——如果派生类显式定义自己的复制构造函数或赋值操作符,则该定义将完全覆盖默认定义。
——被继承类的复制构造函数和赋值操作符负责对基类成分以及类自己的成员进行复制或赋值。
@学习摘录183:派生类析构函数
——析构函数的工作与复制构函数和赋值操作符不同:派生类析构函数不负责撤销基类对象的成员。
——每个析构函数只负责清除自已的成员,对象的撤销顺序与构造顺序相反:首先运行派生类析构函数,然后按继承层次依次向上调用各基类析造函数。
——如果析构函数为虚函,那么通过指针调用时,运行哪个析构函数将因指针所指对象类型的不同而不同。
——即使析构函数没有工作要做,继承层次的根类也应该定义一个虚件构函数。
@学习摘录184:构造函数和赋值操作符不是虚函数
——在复制控制成员中,只有析构函数应定义为虚函数,构造函数不能定义为虚函数。
——将类的赋值操作符设为虚函数很有可能会令人混淆,而且不会有什么好处。
第五节:继承情况下的类作用域——在继承情况下,派生类的作用域嵌套在基类作用域中。如果不能在派生类作用域中确定名字,就在外围基类作用域查找该名字的定义。
@学习摘录185:名字冲突与继承
——与基类成员同名的派生类成员将屏蔽对基类成员的直接访问。
——可以使用作用域操作符访问被屏蔽的基类成员。
@学习摘录186:纯虚函数
——含有(或继承)一个或多个纯虚函数的类是抽象基类(abstract base class)。
——除了作为抽象基类的派生类的对象的组成部分,不能创建抽象类型的对象。
@学习摘录187:容器与继承
——因为派生类对象在赋值给基类对象时会被“切掉”,所以容器与通过继承相关的类型不能很好地融合。
第八节:句柄类与继承
——C++中面向对象编程中一个颇具讽刺意味的地方是,不能使用对象支持面对对象编程,相反,必须使用指针或引用。 Good ! 讽刺意味。。。
@学习摘录188:定义包装类和句柄类
——C++中一个通用的技术是定义包装(cover)类或句柄(handle)类。
——句柄类,存储和管理基类指针。
——指针所指对象的类型可以变化,它既可以指向基类类型对象又可以指向派生类型对象。
@学习摘录189:包装了继承层次的句柄有两个重要的设计考虑因素:
——1. 像对任何保存指针的类一样,必须确定对复制控制做些什么。
——2. 句柄类决定句柄接口屏蔽还是不屏蔽层次,如果不屏蔽层次,用户必须了解和使用基本层次中的对象。