首页 > 代码库 > 泛型和面向对象C++

泛型和面向对象C++

1. 在类内部定义的函数默觉得inline,内联函数应该在头文件里定义,由于其定义对编译器必须是可见的,以便编译器可以在调用点内联展开该函数的代码。

此时,仅有函数原型是不够的。

2.assert

3.异常

4.因为流对象不能复制。因此不能存储在容器中;因为流不能复制。因此形參或返回类型也不能为流类型,必须用指针或引用。对IO对象的读写会改变它的状态,因此引用必须是非const的。

5.假设须要重用文件流读写多个文件,必须在读还有一个文件之前调用clear清除该流的状态。

6.前向声明。

在声明之后。定义之前。类是一个不全然类型。即已知它是一个类型,但不知道包括哪些成员。

不全然类型仅仅能以有限方式使用。

不能定义该类型的对象。不全然类型仅仅能用于定义指向该类型的指针和引用,或者用于声明(而不是定义)使用该类型作为形參类型或返回类型的函数。

在创建类的对象之前。必须完整地定义该类。必须定义类,而不仅仅是声明类,这样,编译器就会给类的对象预定对应的存储空间。相同地,在使用引用或指针范文类的成员之前,必须定义类。

7.不能从const成员函数返回指向类对象的普通引用。const成员函数仅仅能返回*this作为一个const引用。

8.引用全局变量

int height;
void dummy(int height)
{
    ::height = 1;
}
函数中设定全局变量height为1而不是參数height为1.

9.必须对不论什么const或引用类型成员以及没有默认构造函数的类类型的不论什么成员使用初始化式。

10.依照与成员声明一致的次序编写构造函数初始化列表是个好主意。此外,尽可能避免使用成员来初始化其它成员。

11.实际上,假设定义了其它构造函数,则提供一个默认构造函数差点儿总是对的。通常在默认构造函数中给成员提供的初始值应该指出该对象时“空”的。

12.友元不是授予友元关系的那个类的成员,所以它们不受其声明出现部分的訪问控制影响。通常。将友元声明成组地放在类定义的開始或结尾是个好主意。友元能够是普通的非成员函数,或前面定义的其它类的成员函数。或整个类。

13.static函数没有this指针,不是不论什么对象的组成部分,不能声明为const。不能声明为虚函数。

14.static数据成员必须在类定义体的外部定义

15.拷贝构造函数可用于初始化顺序容器中的元素,如

vector<string> svec(5);
编译器首先使用string默认构造函数创建一个暂时值来初始化svec,然后使用拷贝构造函数将暂时值拷贝到svec的每一个元素。

16.为了防止复制,类必须显式声明其拷贝构造函数为private。假设想要连友元和成员的复制也禁止。就能够声明一个private拷贝构造函数但不正确其定义。这样在链接时导致错误。

17.不同意复制的类对象仅仅能作为引用传递给函数或从函数返回。它们也不能用作容器的元素。

18.容器中的元素总是依照逆序撤销,先撤销下标为size()-1的元素。最后是下标为0的元素。

19.三法则:指的是假设须要析构函数,则也须要赋值操作符和拷贝构造函数。

20。合成析构函数并不删除指针成员所指向的对象。

21.析构函数没有返回值,没有形參。由于不能指定不论什么形參,所以不能重载析构函数。尽管一个类能够定义多个构造函数,但仅仅能提供一个析构函数。应用于类的全部对象。

22.析构函数与拷贝构造函数或赋值操作符之间的一个重要差别是,即使我们编写了自己的析构函数,合成析构函数仍然执行。如我们编写了一个空的析构函数。则类的各成员还能够被合成析构函数撤销。

合成析构函数在自己定义析构函数之后执行。

23.大多数C++类採用下面三种方法之中的一个管理指针成员:

(1)指针成员採取常规指针型行为。这种类具有指针的全部缺陷但无需特殊的复制控制。

(2)类可以实现所谓的“智能指针”行为。

指针所指向的对象是共享的。但类可以防止悬垂指针。

(3)类採取值型行为。

指针所指向的对象时唯一的,由每一个类对象独立管理。

24.不能重载的操作符

::     .*    .    ?

:

25.重载操作符必须具有至少一个类类型或枚举类型的操作数。这条规则强制重载操作符不能又一次定义用于内置类型对象的操作符含义。

26.操作符的优先级、结合性或操作数数目不能改变。除了函数调用操作符operator()之外,重载操作符时使用默认实參是非法的。

27.作为类成员的重载函数。形參看起来比操作数少1.作为成员函数的操作符有一个隐含的this形參,限定为第一个操作数。

一般将算术和关系操作符定义为非成员函数,而将赋值操作符定义为成员。

28.重载逗号、取地址、逻辑与、逻辑或等操作符通常不是好做法。这些操作符具有实用的含义,假设我们定义了自己的版本号,就不能使用这些内置含义。

29.当一个重载操作符含义不明显时。给操作取一个名字更好。对于非常少用的操作,使用命名函数通常比用操作符更好。假设不是普通操作,没有必要为简洁而使用操作符。

30.管理容器键类型和顺序容器的类型应定义==和<操作符,理由是很多算法假定这些操作符存在。比如sort算法使用<操作符。find算法使用==操作符。

31.类定义下标操作符时。一般须要定义两个版本号:一个为非const成员并返回引用,还有一个为const成员并返回const引用。

32.类型转换函数必须是成员函数,不能指定返回类型,而且形參表必须为空。尽管转换函数不能指定返回类型,可是每一个转换函数必须显式返回一个指定类型的值。比如,operator int返回一个int值。转换函数一般不应该改变被转换的对象。

因此,转换操作符通常应定义为const成员。

33.类类型转换之后不能再跟还有一个类类型转换。假设须要多个类类型转换,则代码将出错。

34.派生类仅仅能通过派生类对象訪问其基类的protected成员。派生类对其基类类型对象的protected成员没有特殊訪问权限。

35.派生类虚函数调用基类版本号时。必须显式使用作用域操作符。假设派生类函数忽略了这样做,则函数调用会在执行时确定而且将是一个自身调用,从而导致无穷递归。

36.private继承时能够在派生类的public部分使用using Base::XX的形式。使得基类的私有成员能够被用户訪问。

37.使用class保留字定义的派生类默认具有private继承,而用struct保留字定义的类默认具有public继承。

38.友元关系不能继承。基类的友元对派生类的成员没有特殊訪问权限。假设基类被授予友元关系,则仅仅有基类具有特殊訪问权限,该基类的派生类不能訪问授予友元关系的类。

39.假设基类定义了static成员,则整个继承层次中仅仅有一个这种成员。不管从基类派生出多少个派生类,每一个static成员仅仅有一个实例。

40.构造函数仅仅能初始化其直接基类的原因是每一个类都定义了自己的接口。派生类构造函数不能初始化基类的成员且不应该对其基类成员赋值。
41.与构造函数不同,派生类析构函数不负责撤销基类对象的成员,编译器总是显式调用派生类对象基类部分的析构函数。每一个析构函数仅仅负责清楚自己的成员。

42.即使没有工作要做。继承层次的根类也应该定义一个虚析构函数。

43.在复制控制中,仅仅有析构函数能够定义为虚函数,构造函数不能定义为虚函数。构造函数是在对象全然构造之前执行的。在构造函数执行的时候,对象的动态类型还不完整。将赋值操作符定义为虚函数将在派生类中定义一个參数为基类对象的operator=,与派生类中赋值操作符不符合。因此,将类的赋值操作符定义为虚函数会令人混淆,并且不会有什么用处。

44.假设在构造函数或析构函数中调用虚函数。则执行的是为构造函数或析构函数自身定义类型的版本号。

45.在继承情况下,派生类的作用域嵌套在基类作用域中。

46.对象、引用或指针的静态类型决定了对象可以完毕的行为。甚至当静态类型和动态类型可能不同的时候。就像使用基类类型的引用或指针时可能发生的,静态类型仍然决定着可以使用什么成员。

47.在派生类作用域中派生类成员函数将屏蔽基类成员。即使函数原型不同,基类成员也会被屏蔽。

48.假设派生类想通过自身类型使用全部重载版本号,则派生类必需要么重定义全部重载版本号,要么一个也不重定义。

若不想重定义全部,能够为重载成员提供using声明。一个using声明仅仅能指定一个名字。不能指定形參表。因此能够将该函数的全部重载实例加到派生类的作用域。

49.虚函数必须在基类和派生类中拥有同一原型。

50.C++ primer P501。派生类的同名函数可能会将基类的虚函数屏蔽。进而无法通过派生类对象调用,能够通过指向派生类对象的基类引用或指针调用。

51.由于派生类对象在赋值给基类对象时会被“切掉”。所以容器与通过继承相关的类型不能非常好地融合。

52.面向对象编程所依赖的多态性称为执行时多态性。泛型编程所依赖的多态性称为编译时多态性或參数式多态性。

53.用作模板形參的名字不能在模板内部重用,这一限制还意味着模板形參的名字仅仅能在同一模板形參中使用一次。

54.在模板成员名前加上keywordtypename作为前缀,能够告诉编译器将成员当作类型。

55.数组形參能够声明为数组的引用。假设形參是数组的引用,编译器不会将数组实參转化为指针,而是传递数组的引用本身。在这样的情况去,数组大小成为形參和实參类型的一部分。编译器检查数组实參的大小与形參的大小是否匹配。

56.显式模板实參从左至右与相应模板形參相匹配。

57.当编译器看到模板定义的时候,它不马上产生代码。仅仅有在看到用到模板时。如调用了函数模板或调用了类模板的对象的时候,编译器才产生特定类型的模板实例。

58.在包括编译模型中。编译器必须看到用到的全部模板的定义。

59.非类型模板实參必须是编译时常量表达式。

60.特化和部分特化(偏特化)能够具有与通用类模板全然不同的成员集合。

61.函数模板能够重载:能够定义有同样名字但形參数目或类型不同的多个函数模板。也能够定义与函数模板有同样名字的普通非模板函数。

62.异常对象通过复制被抛出表达式的结果创建,该结果必须是能够复制的类型。

63.抛出指针一般是个坏主意:抛出指针要求在相应处理代码存在的随意地方存在所指向的对象。

64.栈展开期间,释放局部对象所用的内存并执行类类型局部对象的析构函数。

65.在为某个异常进行栈展开的时候,析构函数假设又抛出自己的未经处理的还有一个异常,将会导致调用标准库terminate函数。一般而言,terminate函数将调用abort函数,强制从整个程序非正常退出。

66.与析构函数不同,构造函数内部所做的事情常常会抛出异常,因此要保证适当地撤销已构造的成员。

67.假设找不到匹配的catch,程序就调用库函数terminate。

68.catch捕获的类型必须是已定义的,类型的前向声明不行。

69.通常,假设catch字句处理因继承而相关的类型的异常,它就应该将自己的形參定义为引用。

70.假设catch(...)与其它catch子句结合使用。它必须是最后一个。否则。不论什么跟在它后面的catch子句都将不能被匹配。

71.构造函数要处理来自构造函数初始化式的异常,唯一的方法是将构造函数编写为函数測试块。

72.异常安全指即使发生异常程序也能正常操作,即被分配的不论什么资源都适当地释放。通过定义一个类来封装资源的分配和释放,能够保证释放资源。这一技术常称为“资源分配即初始化”,简称RAII。

应该设计资源管理类。以便构造函数分配资源而析构函数释放资源。

73.autoi_ptr仅仅能用于管理从new返回的一个对象。它不能管理动态分配的数组。

当auto_ptr被复制或赋值的时候,有不平常的行为,因此,不能将auto_ptr存储在标准库容器类型中。auto_ptr的复制和赋值改变右操作数,因此,赋值的左右操作数必须都是可改动的左值。

74.应该仅仅用get询问auto_ptr对象或者使用返回的指针值。不能用get作为创建其它auto_ptr对象的实參。

75.auto_ptr对象与内置指针的还有一个差别是,不能直接将一个地址9或者其它指针)赋给auto_ptr对象

76.auto_ptr缺陷:

技术分享

77.假设一个函数声明没有指定异常说明。则该函数能够抛出随意类型的异常。

78.在编译的时候,编译器不能也不会试图验证异常说明。假设函数抛出了没有在其异常说明中列出的异常,就调用标准库函数unexpected。

默认情况下,unexpected函数调用terminate函数,terminate函数通常会终止程序。

79.由于不能再编译时检查异常说明,异常说明的应用一般是有限的。异常说明实用的一种重要情况是。假设函数能够保证不会抛出不论什么异常,对函数的用户和编译器都有所帮助。

80.派生类虚函数异常说明必须与相应基类虚函数的异常说明相同严格,或者比后者更受限。这个限制保证,当使用指向基类类型的指针调用派生类虚函数的时候,派生类异常说明不会添加新的可抛出异常。

81.在用还有一指针初始化带异常说明的函数的指针。或者将后者赋值给函数地址的时候。两个指针的异常说明不必同样。可是,源指针的异常说明必须至少与目标指针的一样严格。

82.命名空间能够在全局作用域或其它作用域内部定义。但不能在函数或类内部定义。

命名空间作用域不能以分号结束。

83.命名空间能够在几个部分中定义。命名空间由它的分离定义部分的总和构成。命名空间是累积的。

84.未命名的命名空间与其它命名空间不同。未命名的命名空间的定义局部于特定文件,从不跨越多个文本文件。

在命名空间引入C++之前,採用static声明局部于文件的名字。

85.假设头文件定义了未命名的命名空间,那么,在每一个包括该头文件的文件里,该命名空间中的名字将定义不同的局部实体。

86.接受类类型形參(或类类型指针及引用形參)的函数(包含重载操作符),以及与类本身定义在同一命名空间中函数(包含重载操作符),在用类类型对象(或类类型的引用及指针)作为实參的时候是可见的。

87.为了提供命名空间中所定义模板的自己的特化。必须保证在包括原始模板定义的命名空间中定义特化。

88.在虚派生中,由最底层派生类的构造函数初始化虚基类。不管虚基类出如今继承层次中不论什么地方,总是在构造非虚基类之前构造虚基类。

89.sort、sort_heap排序默认使用less。为递增排序。make_heap默认使用less,为最大堆。大的在前,小的在后。。

90.模板函数是函数模板的一个实例。

91.函数不能偏特化。

92.除非我有一个好理由同意构造函数被用于隐式类型转换。否则我会把他声明为explicit。


93.

Widget w2 = w1; //调用copy构造函数,而非copy assignment运算符





泛型和面向对象C++