2010-11-9(晚上 类)
最简单地说,类就是定义了一个新的类型和一个新的作用域。
在类内部定义的函数默认为inline.
Const成员(函数)不能改变其所操作的对象的数据成员。Const必须同时出现在声明和定义中,若只出现在其中一处,就会出现一个编译时错误。
类背后蕴含的基本思想是数据抽象和封装。数据抽象是一种依赖于接口和实现分离的编程(和设计)技术。……封装是一项将低层次的元素组合起来形成新的,高层次实体的技术。
在C++中,使用访问标号来定义类的抽象接口和实现封装。……类型的数据抽象视图由其public成员定义。……private封装了类型的实现细节。
并非所有类都必须是抽象的。……一些类,例如pair,确实没有抽象接口。……尽管如此,这样的类型通常还是有成员函数的。
设计类的接口是,设计者应该考虑的是如何方便类的使用;使用类的时候,设计者就不应该考虑类如何工作。
在类的外部定义inline的一个好处是可以使得类比较容易阅读。
不完全类型只能用于定义指向该类型的指针及引用,或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数。
因为只有当类定义体完成后才能定义类,因此类不能具有自身类型的数据成员。然后,只要类名一出现就可以认为该类已声明。因此,类的数据成员可以是指向自身的指针或引用。
定义对象时,将为其分配存储空间,但(一般而言)定义类型时不进行存储分配。
未知时间(类)
public的类型别名杂类体外使用需要作用域限定符;在类体外成员函数定义中,出现成员名之后,不用限定符。
Inline成员函数在类体成员类外声明一次即可。类体内定义的成员函数默认为inline。
Const成员函数不能修改非mutable的数据成员。
Const类对象只能调用const成员函数。
成员函数可以返回本类对象及引用,指针。返回对象时可以设定const属性。
定义类的时候不能具有自身类型的数据成员,但可以是指向自身类型的指针或引用。
一旦遇到右花括号,类的定义就结束了。
未知时间(同上)
const构造函数是不必要的。
构造函数的工作是初始化对象。
构造函数初始化式只在构造函数的定义中而不是声明中指定。
从概念上讲,可以认为构造函数分两个阶段执行:(1)初始化阶段;(2)普通的计算阶段。
在构造函数初始化列表中没有显示提及的每个成员,使用与初始化变量相同的规则来进行初始化。运行该类型的默认构造函数,来初始化类类型的数据成员。内置或复合类型的成员初始化值依赖于对象的作用域:在局部作用域中这些成员不被初始化,而在全局作用域中它们被初始化为0。
必须对任何const或引用类型的成员以及没有默认构造函数的类类型的任何成员使用初始化式。
构造函数初始化列表仅指定用于初始化成员的值,并不指定这些初始化执行的次序。成员被初始化的次序就是定义成员的次序。
初始化的次序常常无关紧要。然而,如果一个成员是根据其他成员而初始化,则成员初始化的次序是至关重要的。
安装与成员声明一致的次序编写构造函数初始化列表是个好主意。此外,尽可能避免使用成员来初始化其他成员。
初始化式可以是任意表达式。
只要定义一个对象时没有提供初始化式,就使用默认构造函数。为所有形参提供默认实参的构造函数也定义了默认构造函数。
实际上,如果定义了其他构造函数,则提供一个默认构造函数几乎总是对的。
可以用单个实参来调用的构造函数定义了从形参类型到该类型的一个隐式转换。
当构造函数被声明为explicit时,编译器将不使用它作为转换操作符。
任何构造函数都可以用来显示地创建临时对象。
通常,除非有明显的理由想要定义隐式转换,否则,但形参构造函数应该为explicit。将构造函数设置为explicit可以避免错误,并且当转换有用时,用户可以显示地构造对象。
每个static数据成员是与类关联的对象,并不与该类的对象相关联。
Static成员函数没有this形参,它可以直接访问所属类的static数据成员,但不能直接使用非static成员。
因为static成员不是任何对象的组成部分,所以static成员函数不能被声明为const。毕竟,将成员函数声明为const就是承诺不会修改该函数所属的对象。
Static数据成员的类型可以是该成员所属的类类型。Static数据成员可用作默认实参。
友元声明将已命名的类或非成员函数引入到外围作用域中。用友元引入的类名和函数(定义或声明),可以像预先声明一样使用。
————————————————————————————————————————————————已打印———————————————————————————————————————————————————
2010-11-26晚(复制控制)
1,复制构造函数,赋值操作符和析构函数总称为复制控制。
2,实现复制控制操作最困难的部分,往往在于识别何时需要覆盖默认版本。
3,c++支持两种初始化形式:直接初始化和复制初始化。
4,合成复制构造函数直接复制内置类型成员的值,类类型成员使用该类的复制构造函数进行复制。数组成员的复制是个例外。虽然一般不能复制数组。但如果一个类具有数组成员,则合成复制构造函数将复制数组。
5,为了防止复制,类必须显式声明其复制构造函数为private。想要连友元和成员中的复制也禁止,就可以声明一个(private)复制构造函数但不对其定义。
6,通过声明(但不定义)private复制构造函数,可以禁止任何复制类类型对象的尝试:用户代码中的复制尝试将在编译时标记为错误。而成员函数和友元函数中的复制尝试将在链接时导致错误。
7,如果定义了复制构造函数,也必须定义默认构造函数。
8,实际上,应将这两个操作(复制和赋值)看做是一个单元。如果需要其中一个,我们几乎也可以肯定需要另一个。
9,变量在超出作用域时应该自动撤销……动态分配的对象只有在指向该对象的指针被删除时才撤销。……撤销一个容器(不管是标准库容器还是内置数组)时,也会运行容器中的类类型元素的析构函数)
10,析构函数通常用于释放再构造函数或在对象生命期内获取的资源。
11,如果类需要析构函数,则它也需要赋值操作符和复制构造函数,这是一个有用的经验法则。这个规则常称为三法则,指的是如果需要析构函数,则需要所有这三个复制控制成员。
12,与复制构造函数或赋值操作符不同,编译器总是会为我们合成一个析构函数。
13,合成析构函数并不删除指针成员所指向的对象。
14,析构函数与复制构造函数或赋值操作符之间的一个重要区别是,即使我们编写了自己的析构函数,合成析构函数仍然运行。
15,复制控制的赋值操作符接收单个形参,且该形参是同一类类型对象。
2011-4-4(中午)重载操作符与转换
1,用于内置类型的操作符,其含义不能改变。
2,操作符的优先级,结合性或操作数数目不能改变。
3,重载操作符并不保证操作数的求值顺序。
4,一般将算术和关系操作符顶以为非成员函数,而将赋值操作符定义为成员。
5,操作符定义为非成员函数时,通常必须将他们设置为所操作类的友元。
6,重载逗号,取地址,逻辑与,逻辑或等操作通常不是好做法。这些操作具有有用的内置含义,如果定义了自己的版本,就不能再使用这些内置含义。
7,赋值,下标,调用(()),和成员访问箭头等操作符必须定义为成员,将这些操作符定义为非成员函数将导致编译时错误。
8,对称的操作数,如算术操作符,相等操作符,关系操作符和位操作符,最好定义为普通非成员函数。
9,当定义符合标准库iostream规范的输入或输出操作符的时候,必须使它成为非成员操作符。
10,与输出操作符类似,输入操作符的第一个形参是一个引用,指向它要读的流,并且返回的也是对同一个流的引用。它的第二个形参是对要读入的对象的非const引用,该形参必须为非const,因为输入操作符的目的是将数据读到这个对象中。
11,更重要但通常重视不够的是,输入和输出操作符有如下区别:输入操作符必须处理错误和文件结束的可能性。
12,设计输入操作符时,如果可能,要确定错误恢复措施,这很重要。
13,注意,为了与内置操作符保持一致,加法返回一个右值,而不是一个引用。
14,类赋值操作符必须是类的成员,以便编译器可以知道是否需要合成一个。
15,下标操作符必须定义为类成员函数。
16,类定义下标操作符时,一般需要定义两个版本:一个为非const成员并返回引用,另一个为const成员并返回const引用。
17,箭头操作符必须定义为类成员函数。解引用操作符不要求定义为成员,但将它作为成员一般也是正确的。
18,重载箭头操作符必须返回指向类类型的指针,或者返回定义了自己的箭头操作符的类类型对象。
19,C++语言不要求自增操作符或自减操作符一定作为类的成员,但是,因为这些操作符改变对象的状态,所以更倾向于将它们作为成员。
20,为了与内置类型一致,前缀式操作符应返回被增量或减量对象的引用。
21,后缀式操作符函数接受一个额外的(即无用的)int型形参。
22,为了与内置操作符一致,后缀式操作符应返回旧值,并且,应作为值返回,而不是返回引用。
23,如果想要使用函数调用来调用后缀式操作符,必须给出一个整型实参值。
ps:做这些笔记的时候是一年前多了。当时还在做着ACM,学这些似乎只是抱着充实自己的心态,当时最想的似乎就是看完整本书,但最终还是在面向对象的部分停下来了。现在很多东西都忘了,毕竟这样学而没有实际使用,肯定很容易忘的。可能当时也是出现了这么个想法而停下来的吧,至少应该是其中一个原因。这份笔记似乎在硬盘里丢失过一次,最后在金山快盘里找回来的。2011-12-4