C++primer(第四版)复习笔记—第三篇: 类和数据抽象

数据抽象:是指定义数据和函数成员的能力;

封装:是指从常规访问中保护类成员的能力。

接口:成员函数定义了类的接口。通过将定义类所用到的数据和成员函数设置维为private来封装类。

第十二章: 类

1、 构造函数的初始化式只在构造函数的定义中而不是声明中指出。

2、 使用构造函数的初始化列表与在构造函数体中对类的成员变量进行赋值的区别:本质就在于前者是对变量进行初始化,而后者是对变量进行赋值。

理解:构造函数的执行分为两个阶段:先初始化阶段,再是普通计算阶段。

在初始化阶段构造函数将调用类类型的构造函数(没有初始化列表时调用默认构造函数)对类类型的成员变量进行初始化,在普通计算阶段才执行构造函数体的语句,因此在这时是对成员变量进行赋值,覆盖初始化阶段的初始值。 对内置类型,根据对象定义的位置不同,初始化阶段会不同(对全局对象,会将内置类型初始化为0,而局部变量则不初始化); 另外,对于那些const对象成员、引用成员、没有默认构造函数的类类型(否则会调用其默认构造函数,但其没有)等,则必须提供初始化列表进行显示初始化。

3、 建议构造函数使用初始化列表:一是效率:免去了普通计算阶段的复制过程;二是:对于那些const对象成员、引用成员、没有默认构造函数的类类型(否则会调用其默认构造函数,但其没有)等,则必须提供初始化列表进行显示初始化。

4、 注意:成员初始化的次序与成员定义的次序相同(而不是按在初始化列表中出现的次序进行),所以应尽量按定义的次序来给出初始化列表,并注意用一个成员来初始化其他成员时的先后问题。

5、 一个类只有没有定义任何构造函数时才会自动生成合成的默认构造函数。对类类型调用其默认构造函数进行初始化;而对内置类型,当对象定义在全局时才进行初始化,在局部对象中不进行初始化。

6、 如果类包括内置或复合类型(指针 引用)等成员,则不应依赖于合成的默认构造函数,应该定义自己的构造函数来初始化这些变量。

7、 类通常应该定义一个默认构造函数,并且在默认构造函数中给成员提供的初始应该指出对象时“空”的。

8、 为所有形参提供默认实参的构造函数也定义了默认构造函数。

9、 默认构造函数的使用误区:使用类A的默认构造函数定义对象:

A a();//这是错误的,因为这会被编译器解释为定义了一个参数为空,返回一个对象A的函数。因此用默认构造函数时不能在后面带括号(带有参数的非默认构造函数则可以带括号,在括号里给出实参值)。但这种用法是对的:A a=A();//右边调用默认构造函数创建对象,并用该对象初始化对象a.

10 、隐式类类型转换:用单个实参来调用的构造函数定义了从参数类型到类类型的以个隐式转换。如此,可在需要一个类类型的地方传递一个该参数类型,从而编译器会调用该构造函数并以该参数为实参构造一个所需的类类型的临时对象。 但注意:这种隐式转换是否为我们所需要的!否则应该避免。

11、 抑制由构造函数定义的隐式转换: 将构造函数声明为explict来防止构造函数被用作隐式类型转换之用: 在构造函数声明前加上关键字explicit(在类外的构造函数定义体上不能再加explicit)。 此时编译器不不再使用构造函数作为类型转换(编译器会报错)。

12、 为转换而显示的使用构造函数: 任何构造函数(包括默认)都可以显示的创建临时对象,即类名后面直接带括号,括号里面给出要调用的构造函数的参数,而不给出对象名。(如:A();//创建一个无名的临时对象)。

13、 通常,除非用明确的理由需要隐式转换,否则单参数的构造函数前都应该加上explict以避免错误。 当需要类型转换时可用显示使用构造函数来显示的创建临时对象。

13、 类成员的显示初始化:当没有定义构造函数,且所有数据成员都为共有时,可按初始化数组元素的方式来初始化所有数据成员

如: struct A { int i; int* p};

A a={0,0};//a.i=0; a.p=o; (根据数据成员的声明次序进行初始化)

14、 友元机制允许一个类将其非共有成员的访问权授予指定的函数或类。 Friend只能出现在类内部。 友元声明可以在类中的任意位置:友元不是该类的成员,u因此它们不受其声明出现部分的访问控制影响。

15、 Static类成员:是类的组成部分,而不是某个对象的组成部分(为类的全体对象共有,因此可用于在类的全体对象间传递信息,如记录创建了多少个该类的对象)。Static的成员,只用在类中声明时指定static,在内外定义时不用重复指定为static。

16、 static数据成员:存在于类类型的每个对象中,且独立于该类的某个特定对象而存在,与该类关联,而非该类的某个对象。 Static数据成员必须在类外部定义(正好一次),因为其不是通过类的构造函数来初始化的,而是在定义时进行初始化。 常在类的非内联函数的定义文件中定义static数据成员(不用再重复指定为static),定义时与定义一般成员函数一样,需要在类型后,变量明前指定完全限定名,以指定属于哪个类,如:

int A::static_member=10;(注,只要出现全完限定名,其后面的内容就是在类作用域中。) 特殊的:对cosnt static int 数据成员,只要其初始化式是一个常量表达式,就可以直接在类中进行初始化,(但其他类型的cosnt static数据成员,还是需要在类外部进行定义初始化):

class A{
private:
static const int a =30; //直接在类定义体中进行用常量表达式初始化
static const string str;//不能在类中进行初始化。
};

Static函数成员:没有this参数,可以直接访问所属类的静态成员,但不能直接使用非静态成员。

17、 static成员遵循正常的访问权限。 Static成员函数不能声明为const函数(因为const成员函数是为不修改其对象),其不能被声明为虚函数。Static数据成员可定义为任意数据类型。

18、 static成员可以像其他普通成员那样被对象引用,也可以直接使用作用域操作符直接以类调用:A::static_member.。 (static成员非对象所有,因此可以独立于对象而被使用)。 如:Static数据成员的类型可以是其类类型。

第十三章: 复制控制

1、 构造函数 和 复制控制成员(复制构造函数、赋值操作符函数、析构函数)不能被继承,但在派生类中能调用基类的这些函数,且不能定义为虚函数(除析构函数外),(赋值操作符函数:因为不能不被继承,所以派生类一定有自己的版本,因此定义为虚函数无意义;构造函数:在对象构造之前运行,此时对象的动态类型还不完整,因此定义为虚函数无意义)。每个类定义自己的构造函数和复制控制成员,如果不定义就使用合成版本。 派生类中如果自己定义了构造函数或复制控制成员,若没有显示调用直接基类的构造函数或复制控制成员,则会使用基类的默认版本来构造或复制基类部分(派生类的构造函数可在初始化列表中显示指出,而赋值操作符可在函数体中显示调用基类版base::operator=(rhs)本来完成基类部分)。 注意:赋值操作符中需要检查是否为自我赋值:

Drivied& Drivied::operator=(Derivied& rhs)
{
    if(*this!=rhs){
     base::operator=(rhs);  //赋值基类部分
    //继续复制派生类自己定义的成员
    }
    Return *this;
}

2、 只包含类类型、内置类型类可以不用定义自己的构造函数和复制控制成员以及析构函数;如果有指针成员则不能再依赖于合成版本。

3、 动态绑定只会发生在基类的引用或指针(可绑定到基类或派生类对象)在调用虚函数时。 可用派生类来给基类赋值或构造基类(基类的赋值构造函数),因为复制构造函数和赋值操作符的形参为基类的const引用,因此可绑定到派生类(该过程派生类对象发生假切割,即丢弃了派生自定义成员部分)。注意:虽然基类指针或引用可绑定到派生类对象(实际是其中的基类部分),还是只能访问派生类对象中的基类部分

4、 派生类析构函数不负责撤销基类对象的成员。编译器总是显示调用派生类对象基类部分的析构函数。每个析构函数只负责清除自己的成员。对象的撤销顺序与构造相反:先运行派生类的析构函数,然后依次向上调用各基类的析构函数。

5、 虚析构函数:析构函数可定义为虚函数,且虚性质可被继承(但析构函数本身不能被继承)。当动态析构一个基类指针时,根据指针的动态类型来自动选择调用基类的析构函数还是派生类析构函数(因为析构函数声明为虚函数)。 因此就算基类的析构函数什么也不需要做,也需要定义一个虚析构函数。

6、 在构造与析构派生类对象期间,派生类对象的类型是变化的:在构造时,首先构造其基类部分,此时为基类类型;在析构时先析构其派生类部分,在调用基类析构函数时只剩下基类部分,因此为基类类型。

7、 重载、覆盖与隐藏

1).重载:成员函数具有以下的特征时发生”重载”

A.相同的范围(同一个类中)

B.函数的名字相同

C.参数类型不同(不能进行隐式类型转换)

D.Virtual关键字可有可无

2).覆盖(也叫”继承”):指派生类函数覆盖基类函数,特征是:

A.不同的范围(分别位于基类与派生类中)

B.函数名字相同

C.参数相同

D.基类函数必须有virtual关键字

3).隐藏:是指派生类的函数屏蔽了与其同名(参数列表,返回类型无关)的基类函数,规则如下:

A.如果派生类的函数与基类的函数同名,但是参数不同,此时不论有无virtual关键字,基类的函数都将被隐藏,注意别与重载混淆)

B.如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字,此时基类的函数被隐藏

被隐藏的基类成员,派生类对象可通过基类名加作用域符来显示说明该处要调用基类的该成员。 在派生类中被隐藏的基类版本成员是存在的,只是被隐藏,不能通过派生类对象调用。

所以,隐藏(不同作用域)规则的底层原因其实是C++的名字解析过程(即先在派生类域中查找该名字的成员,一旦找到就停止,如果没有才会继续到基类域中查找。 重载必须在

同一作用域中,查找时是查找最优匹配)

覆盖规则造成的调用现象,其实就是类的虚函数实现原理生成的

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

时间: 2024-08-02 20:18:39

C++primer(第四版)复习笔记—第三篇: 类和数据抽象的相关文章

C++ Primer 第四版读书笔记(三)之数组与指针

C++语言提供了两种类似于vector和迭代器类型的低级复合类型--数组与指针.与vector类型相似,数组也可以保存某种类型的一组对象:而它们的区别在于,数组的长度是固定的.数组一经创建,就不允许添加新的元素.指针则可以像迭代器一样用于遍历和检查数组中的元素. 现代C++程序应尽量使用vector和迭代器类型,而避免使用低级的数组和指针.设计良好的程序只有在强调速度时才在类实现的内部使用数组和指针. 数组是C++语言中类似于标准库vector类型的内置数据结构.与vector类似,数组也是一种

C++Primer第5版学习笔记(三)

C++Primer第5版学习笔记(三) 第四/五章的重难点内容 你可以点击这里回顾第三章内容 因为第五章的内容比较少,因此和第四章的笔记内容合并.   第四章是和表达式有关的知识,表达式是C++的基础设施,本章由三部分组成:         1.表达式概念基础,包括表达式的基本概念,左值和右值的概念,优先级结合律,求值顺序.  2.各种运算符,主要包括算数\关系\逻辑\赋值\递增递减\成员访问\条件\位运算\sizeof\逗号运算符 这10种运算符.  3.类型转换,包括隐式和显式两种转换的规则

C++ Primer 第四版读书笔记(八)之顺序容器

容器容纳特定类型对象的集合. 标准库vector类型,是一个顺序容器.它将单一类型元素聚集起来称为容器,然后根据位置来存储和访问这些元素,这就是顺序容器.顺序容器的元素排列次序与元素值无关,而是由元素添加到容器里的次序决定. 标准库定义了三种顺序容器类型:vector.list和deque.它们的差别在于访问元素的方式,以及添加或删除元素相关操作的运行代价.标准库还提供了三种容器适配器.实际上,适配器是根据原始的容器类型所提供的操作,通过定义新的操作接口,来适应基础的容器类型. 顺序容器类型 顺

C++ Primer 第四版读书笔记(六)之函数

内联函数.类成员函数和重载函数以及函数指针. 函数可以看作程序员定义的操作.与内置操作符相同的是,每个函数都会实现一系列的计算,然后(大多数时候)生成一个结算结果.但与操作符不同的是,函数有自己的函数名,而且操作数没有数量限制.与操作符一样,函数可以重载,这意味着同样的函数名可以对应对个不同的函数. 一.函数的定义 函数由函数名以及一组操作数类型唯一地表示.函数的操作数,也即形参(parameter),在一对圆括号中声明,形参与形参之间以逗号分隔.函数执行的计算在一个称为函数体(function

C++ Primer 第四版读书笔记(七)之标准IO库

C++的输入/输出由标准库提供.标准库定义了一组类型,支持对文件和控制窗口等设备的读写(IO).还定义了其他一些类型,使string对象能够像文件一样操作,从而使我们无需IO就能实现数据与字符之间的转换.一般来说,类的设计者还可以很方便地使用IO标准库设施读写自定义类的对象.类类型通常使用IO标准库为内置类型定义的操作符和规则来进行读写. 一.面向对象的标准库 迄今为止,我们已经使用IO类型和对象读写数据流,它们常用于与用户控制窗口的交互.当然,实际的程序不能仅限于对控制窗口的IO,通常还需要读

C++ Primer 第四版读书笔记(四)之表达式

表达式由一个或多个操作数通过操作符组合而成.最简单的表达式仅包含一个字面值常量或变量.较复杂的表达式则由操作符以及一个或多个操作数构成. 每个表达式都会产生一个结果.如果表达式中没有操作符,则其结果就是操作数本身的值.当一个对象用在需要使用其值的地方,则计算该对象的值. 操作符的含义--该操作符执行什么操作以及操作结果的类型--取决于操作数的类型. C++提供了一元操作符和二元操作符两种操作符.作用在一个操作数上的操作符称为一元操作符.如曲地址符(&)和解引用操作符(*):而二元操作符则作用于两

C++ Primer 第四版读书笔记(五)之语句

通常情况下,语句是顺序执行的.但是,除了最简单的程序外,只有顺序执行往往并不足够.为此,C++定义了一组控制流语句,允许有条件地执行或者重复地执行某部分功能. 1.1 简单语句 C++中,大多数语句以分号结束. 表达式语句用于计算表达式. 1.2 声明语句 在C++中,对象或类的定义或声明也是语句. 1.3 复合语句 复合语句,通常被称为块,使用一对花括号括起来的语句序列.块标识了一个作用域,在块中引入的名字只能在该块内部或者嵌套在块中的子块里访问.通常,一个名字只从其定义处到该块结尾这段范围内

C++ Primer 第四版读书笔记(八)之顺序容器(续)

3.6 访问元素 如果容器非空,那么容器类型的front和back成员将返回容器内第一个或最后一个元素的引用. 访问顺序容器内元素的操作 c.back() 返回容器c的最后一个元素的引用.如果c为空,则该操作未定义 c.front() 返回容器c的第一个元素的引用.如果c为空,则该操作未定义 c[n] 返回下标为n的元素的引用 如果n<0或n>c.size(),则该操作未定义 只适用于vector和deque容器 c.at[n] 返回下标为n的元素的引用.如果下标越界,则该操作未定义 只适用于

C++ Primer 第四版读书笔记(一)之变量和基本类型

C++语言定义了几种基本类型:字符型.整形.浮点型等. 1.基本内置类型 C++定义了一组表示整数.浮点数.单个字符和布尔值的算术类型,另外还定义了一种称为void的特殊类型. 表1.1 C++:算术类型 类型 含义 最小存储空间 bool 布尔型 - char 字符型 8位 wchar_t 宽字符型 16位 short 短整型 16位 int 整形 16位 long 长整形 32位 float 单精度浮点型 6位有效数字 double 双精度浮点型 10位有效数字 long double 扩展