程序员人生 网站导航

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

栏目:php教程时间:2015-04-22 08:15:43

数据抽象:是指定义数据和函数成员的能力;
封装:是指从常规访问中保护类成员的能力。
接口:成员函数定义了类的接口。通过将定义类所用到的数据和成员函数设置维为private来封装类。

第102章: 类

1、 构造函数的初始化式只在构造函数的定义中而不是声明中指出。
2、 使用构造函数的初始化列表与在构造函数体中对类的成员变量进行赋值的区分:本质就在于前者是对变量进行初始化,而后者是对变量进行赋值。
理解:构造函数的履行分为两个阶段:先初始化阶段,再是普通计算阶段。
在初始化阶段构造函数将调用类类型的构造函数(没有初始化列表时调用默许构造函数)对类类型的成员变量进行初始化,在普通计算阶段才履行构造函数体的语句,因此在这时候是对成员变量进行赋值,覆盖初始化阶段的初始值。 对内置类型,根据对象定义的位置不同,初始化阶段会不同(对全局对象,会将内置类型初始化为0,而局部变量则不初始化); 另外,对那些const对象成员、援用成员、没有默许构造函数的类类型(否则会调用其默许构造函数,但其没有)等,则必须提供初始化列表进行显示初始化。
3、 建议构造函数使用初始化列表:1是效力:免去了普通计算阶段的复制进程;2是:对那些const对象成员、援用成员、没有默许构造函数的类类型(否则会调用其默许构造函数,但其没有)等,则必须提供初始化列表进行显示初始化。
4、 注意:成员初始化的次序与成员定义的次序相同(而不是按在初始化列表中出现的次序进行),所以应尽可能按定义的次序来给出初始化列表,并注意用1个成员来初始化其他成员时的前后问题。
5、 1个类只有无定义任何构造函数时才会自动生成合成的默许构造函数。对类类型调用其默许构造函数进行初始化;而对内置类型,当对象定义在全局时才进行初始化,在局部对象中不进行初始化。
6、 如果类包括内置或复合类型(指针 援用)等成员,则不应依赖于合成的默许构造函数,应当定义自己的构造函数来初始化这些变量。
7、 类通常应当定义1个默许构造函数,并且在默许构造函数中给成员提供的初始应当指出对象时“空”的。
8、 为所有形参提供默许实参的构造函数也定义了默许构造函数。
9、 默许构造函数的使用误区:使用类A的默许构造函数定义对象:
A a();//这是毛病的,由于这会被编译器解释为定义了1个参数为空,返回1个对象A的函数。因此用默许构造函数时不能在后面带括号(带有参数的非默许构造函数则可以带括号,在括号里给出实参值)。但这类用法是对的:A a=A();//右侧调用默许构造函数创建对象,并用该对象初始化对象a.
10 、隐式类类型转换:用单个实参来调用的构造函数定义了从参数类型到类类型的以个隐式转换。如此,可在需要1个类类型的地方传递1个该参数类型,从而编译器会调用该构造函数并以该参数为实参构造1个所需的类类型的临时对象。 但注意:这类隐式转换是不是为我们所需要的!否则应当避免。
11、 抑制由构造函数定义的隐式转换: 将构造函数声明为explict来避免构造函数被用作隐式类型转换之用: 在构造函数声明前加上关键字explicit(在类外的构造函数定义体上不能再加explicit)。 此时编译器不不再使用构造函数作为类型转换(编译器会报错)。
12、 为转换而显示的使用构造函数: 任何构造函数(包括默许)都可以显示的创建临时对象,即类名后面直接带括号,括号里面给出要调用的构造函数的参数,而不给出对象名。(如:A();//创建1个无名的临时对象)。
13、 通常,除非用明确的理由需要隐式转换,否则单参数的构造函数前都应当加上explict以免毛病。 当需要类型转换时可用显示使用构造函数来显示的创建临时对象。
13、 类成员的显示初始化:当没有定义构造函数,且所有数据成员都为共有时,可按初始化数组元素的方式来初始化所有数据成员
如: struct A { int i; int* p};
A a={0,0};//a.i=0; a.p=o
; (根据数据成员的声明次序进行初始化)
14、 友元机制允许1个类将其非共有成员的访问权授与指定的函数或类。 Friend只能出现在类内部。 友元声明可以在类中的任意位置:友元不是该类的成员,u因此它们不受其声明出现部份的访问控制影响。
15、 Static类成员:是类的组成部份,而不是某个对象的组成部份(为类的全部对象共有,因此可用于在类的全部对象间传递信息,如记录创建了多少个该类的对象)。Static的成员,只用在类中声明时指定static,在内外定义时不用重复指定为static。
16、 static数据成员:存在于类类型的每一个对象中,且独立于该类的某个特定对象而存在,与该类关联,而非该类的某个对象。 Static数据成员必须在类外部定义(正好1次),由于其不是通过类的构造函数来初始化的,而是在定义时进行初始化。 常在类的非内联函数的定义文件中定义static数据成员(不用再重复指定为static),定义时与定义1般成员函数1样,需要在类型后,变量明前指定完全限定名,以指定属于哪一个类,如:
int A::static_member=10;(注,只要出现全完限定名,其后面的内容就是在类作用域中。) 特殊的:对cosnt static int 数据成员,只要其初始化式是1个常量表达式,就能够直接在类中进行初始化,(但其他类型的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数据成员的类型可以是其类类型。

第103章: 复制控制

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

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

2、 只包括类类型、内置类型类可以不用定义自己的构造函数和复制控制成员和析构函数;如果有指针成员则不能再依赖于合成版本。
3、 动态绑定只会产生在基类的援用或指针(可绑定到基类或派生类对象)在调用虚函数时。 可用派生类来给基类赋值或构造基类(基类的赋值构造函数),由于复制构造函数和赋值操作符的形参为基类的const援用,因此可绑定到派生类(该进程派生类对象产生假切割,即抛弃了派生自定义成员部份)。注意:虽然基类指针或援用可绑定到派生类对象(实际是其中的基类部份),还是只能访问派生类对象中的基类部份
4、 派生类析构函数不负责撤消基类对象的成员。编译器总是显示调用派生类对象基类部份的析构函数。每一个析构函数只负责清除自己的成员。对象的撤消顺序与构造相反:先运行派生类的析构函数,然后顺次向上调用各基类的析构函数。
5、 虚析构函数:析构函数可定义为虚函数,且虚性质可被继承(但析构函数本身不能被继承)。当动态析构1个基类指针时,根据指针的动态类型来自动选择调用基类的析构函数还是派生类析构函数(由于析构函数声明为虚函数)。 因此就算基类的析构函数甚么也不需要做,也需要定义1个虚析构函数。
6、 在构造与析构派生类对象期间,派生类对象的类型是变化的:在构造时,首先构造其基类部份,此时为基类类型;在析构时先析构其派生类部份,在调用基类析构函数时只剩下基类部份,因此为基类类型。
7、 重载、覆盖与隐藏
1).重载:成员函数具有以下的特点时产生”重载”
A.相同的范围(同1个类中)
B.函数的名字相同
C.参数类型不同(不能进行隐式类型转换)
D.Virtual关键字可有可无
2).覆盖(也叫”继承”):指派生类函数覆盖基类函数,特点是:
A.不同的范围(分别位于基类与派生类中)
B.函数名字相同
C.参数相同
D.基类函数必须有virtual关键字
3).隐藏:是指派生类的函数屏蔽了与其同名(参数列表,返回类型无关)的基类函数,规则以下:
A.如果派生类的函数与基类的函数同名,但是参数不同,此时不论有没有virtual关键字,基类的函数都将被隐藏,注意别与重载混淆)
B.如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字,此时基类的函数被隐藏
被隐藏的基类成员,派生类对象可通过基类名加作用域符来显示说明该处要调用基类的该成员。 在派生类中被隐藏的基类版本成员是存在的,只是被隐藏,不能通过派生类对象调用。
所以,隐藏(不同作用域)规则的底层缘由实际上是C++的名字解析进程(即先在派生类域中查找该名字的成员,1旦找到就停止,如果没有才会继续到基类域中查找。 重载必须在
同1作用域中,查找时是查找最优匹配)
覆盖规则酿成的调用现象,其实就是类的虚函数实现原理生成的
8、 含有或继承有1个或多个纯虚函数的类是抽象基类。 抽象基类除可作为其派生类对象的抽象基类组成部份外,不能创建抽象基类的对象。

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐