《C++ Primer》第四部分学习笔记汇总——面向对象编程与泛型编程
——继承,动态绑定,数据抽象,函数模板
《C++ Primer》 第15章学习笔记
第15章:面向对象编程
——面向对象编程基于三个基本概念:数据抽象,继承,动态绑定。
——用类进行数据抽象
——用类派生从一个类继承另一个类:派生类继承基类成员;
——动态绑定使编译器能够在运行时决定是使用基类中定义的函数还是派生类中定义的函数。
@学习摘录162:继承与动态绑定的作用:
——能够容易地定义与其他类相似但又不相同的新类,能够更容易地编写忽略这些相似类型之间区别的程序。
@学习摘录163:多态性
——面向对象编程的关键思想是多态性(polymorphism)
——继承而相关联的类型为多态类型。
@学习摘录164:继承
——派生类(derived class)能够继承基类(base class)定义的成员
——派生类可以无须改变而使用那些与派生类型具体特性不相关的操作
——派生类可以重定义那些与派生类型相关的成员函数,将函数特化,考虑派生类型的特性。
——在C++中,基类必须指出希望派生类重定义哪些函数,定义为virtual的函数是基类期待派生类重新定义的,基类希望派生类继承的函数不能定义为虚函数。
@学习摘录165:动态绑定(dynamic binding)
——我们能够编写程序使用继承层次中任意类型的对象,无须关心对象的具体类型。
@学习摘录166:protected成员
——可以认为protected访问标号是private和public的混合:
——1. 像private成员一样,protected成员不能被类的用户访问。
——2. 像public成员一样,protected成员可被该类的派生类访问。
——派生类只能通过派生类对象访问其基类的protected成员,派生类对其基类类型对象的protected成员没有特殊访问权限。
@学习摘录167:派生类类型接口
——简单地说:提供给派生类型接口是protected 成员和public成员的集合。
@学习摘录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:派生类函数调用基类版本
——只有成员函数中的代码才应该使用作用域操作符覆盖虚函数机制。
——覆盖虚函数机制,最常见的理由是为了派生类虚函数调用基类中的版本。
——派生类虚函数调用基类版本时,必须显式使用作用域操作符。如果派生类函数忽略了这样做,则函数调用会在运行时确定并且将是一个自身调用,从而导致无穷递归。
@学习摘录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++中面向对象编程中一个颇具讽刺意味的地方是,不能使用对象支持面对对象编程,相反,必须使用指针或引用。
@学习摘录188:定义包装类和句柄类
——C++中一个通用的技术是定义包装(cover)类或句柄(handle)类。
——句柄类,存储和管理基类指针。
——指针所指对象的类型可以变化,它既可以指向基类类型对象又可以指向派生类型对象。
@学习摘录189:包装了继承层次的句柄有两个重要的设计考虑因素:
——1. 像对任何保存指针的类一样,必须确定对复制控制做些什么。
——2. 句柄类决定句柄接口屏蔽还是不屏蔽层次,如果不屏蔽层次,用户必须了解和使用基本层次中的对象。
《C++ Primer》 第16章学习笔记
第16章:模板与泛型编程
——泛型编程,就是以独立于任何特定类型的方式编写代码。
——使用泛型程序时,我们需要提供具体程序实例所操作的类型或值。
@学习摘录190:补充概念:多态性
——面向对象编程所依赖的多态性称为运行时多态性。
——泛型编程所依赖的多态称为编译时多态性或参数式多态性。
@学习摘录191:定义函数模板
——函数模板(function template)是一个独立于类型的函数,可作为一种方式,产生函数的特定类型的版本。
——模板定义以关键字template开始,后接模板形参表(template parameter list)。
——模板形参表是用尖括号括住的一个或多个模板形参(template parameter)的列表。
——模板形参表不能为空。
@学习摘录192:使用函数模板——实例化
——产生模板的特定类型的过程称为实例化。
——要进行实例化,编译器必须能够访问定义模板的源代码。
——使用函数模板时,编译器会推断哪个模板实参绑定到模板形参。
——一旦编译器确定了实际的模板实参,就称它为实例化(instantiate)了函数模板的一个实例。
@学习摘录193:typename与class的区别
——在函数模板形参中,关键字typename和class相同含义,可互用,只有有时候typename比class要直观。
@学习摘录194:链接时的编译时错误
——编译模板时,编译器可能会在三个阶段中标识错误:
——1. 第一阶段是编译模板定义本身时。普通的语法错误类的。
——2. 第二阶段时在编译器见到模板的使用时,检查实参与形参是否相对映。
——3. 第三阶段是在实例化时,只有在这个时候可以发现类型相关的错误。
@学习摘录195:类模板中的友元声明
——1. 普通非模板类或函数的友元声明,将友元关系授予明确指定的类或函数。
——2. 类模板或函数模板的友元声明,授予对友元所有实例的访问权。
——3. 只授予对类模板或函数模板的特定实例的访问权的友元声明。
-—如:
template
{
// grants access to ordinary, nontemplate class and function
friend class FooBar;
friend void fcn();
};
——FooBar的成员和fcn函数可以访问Bar类任意实例的private成员和protected成员。
摘录有想195:
——假如A类要在B类中使用,那么,要在B类中对A进行友元声明。友元声明不是在需要使用A类时才声明调用,而是在需要使用到的类中先声明。
@学习摘录196:成员模板
——任意类(模板或非模板)可以拥有本身为类模板或函数模板的成员,这种成员称为成员模板(member template),成员模板不能为虚。
第六节:模板特化
——模板特化是指一个或多个模板形参的实际类型或实际值是指定的。
@学习摘录197:模板特化转换方式
——template <> int compare
——当调用compare函数的时候,传给它两个字符指针,编译器将调用特化版本。
@学习摘录198:普通作用域规则适用于特化
——当编译器看到一个函数调用时,它必须知道这个版本需要特化,否则,编译器可能从模板定义实例化该函数。
――对于具有同一模板实参集的同一模板,程序不能既有显式特化又有实例化。
@学习摘录199:显式模板实参
——显式模板实参使我们能固定一个或多个模板形参的类型或值。
——显式实参使我们能够设计无需从对应实参推断模板类型的函数,也使我们能够对实参进行转换。
@学习摘录200:模板特化
——模板特化是一种特化的定义,它定义了模板的不同版本,将一个或多个形参绑定到特定类型或特定值。
——对于默认模板定义不适用的类型,特化非常有用。
@学习摘录201:泛型句柄类(generic handle class)
——保存和管理指向其他类的指针的类。
@学习摘录202:实例化(instantiation)
——用实际模板实参产生模板特定实例的编译器过程,在该实例中,用对应实参代替形参。
@学习摘录203:模板特化(template specialization)
——类模板或类模板的成员的重定义,其中指定了模板形参。