主要学习泛型程序设计、模板、STL、异常处理等以前不是很清楚的内容。
标准C++头文件没有后缀。
编译C++程序时,编译器自动定义了__cplusplus,而对C程序则定义了__STDC__。另外,__LINE__记录文件已经编译的行数,__FILE__包含正在被编译的文件的名字。__TIME__ __DATE__
assert()是C语言标准库提供的,需包含头文件assert.h,它的C++名字是cassert,但这种方式下所有C++库名字都是在std中定义的。
endl在输出流中插入一个换行符,然后刷新输出缓冲区。
string word;
while (cin << word)
上面的代码每次读入一个字符串(一个单词),直到所有串都读进来。
基类的构造函数并没有被派生类继承,析构函数和拷贝赋值操作符同样也没有。
泛型设计就是类型不确定,即可处理多种类型,使用模板即可,我以为是什么呢。
程序异常的抛出与处理位于独立的函数或成员函数调用中。找到处理代码通常要涉及到展开程序调用栈。一旦异常被处理完毕,就恢复正常的程序执行。但不是在发生异常的地方恢复执行过程,而是在处理异常的地方恢复执行过程。
名字空间别名允许用另一个名字与一个现有的名字空间关联起来。如:
namespace LIB = IBM_Canada_Laboratory;
using指示符使名字空间内的所有声明都可见,using声明提供了更为精细的选择。如下:
using namespace std;
using std::string;
泛型算法提供了容器等模板的一些通用操作。汗,刚才以为就是泛型设计呢。
使用泛型算法需要包含头文件algorithm,并使用一对迭代器标记遍历元素的范围。它还能接受指向内置数组的指针对。如下:
vector<int> ivec(8);
sort(ivec.bigin(), ivec.begin() + ivec.size() / 2);
int ia[7] = {0};
sort(ia, ia + 7);
long double:长双精度,三个或四个字长度。
一个数值,如1,称为文字常量。可在后面加U表示无符号整数。如8LU。浮点型文字常量缺省为double型,可在十进制形式后面加f或L表示其它类型。
L’a’表示宽字符。两个相邻的字符串会连接成一个,据此分行写长字符串以免使用/符号产生的tab键也进入字符串中的问题。
int month = 1, day = 10, year = 2006;
int ival = int();
int ival(0);
不允许非const引用指向需要临时对象的对象或值。如
double a = 1;
const int & b = a;
当一个对象的值可能在编译器的控制或监测之外被改变时,那么该对象应该声明成volatile。
内联函数的实现应该放在头文件中。
vector pair
标准C++头文件limits提供了与内置类型有关的信息。
二元关系操作符的左右操作数的计算顺序在标准C和C++中都是未定义的。
前置自增操作符以引用方式返回,而后置自增操作符以传值方式返回,且多了一个不曾使用的int参数。
sizeof操作符返回size_t类型,它在cstddef中定义。
bitset类封装了对位向量的操作,需包含bitset头文件。
隐式转换中的算术转换原则如下:
为防止精度损失,如果必要的话,类型总是被提升为较宽的类型。
所有含有小于整型的整值类型的算术表达式,在计算之前,其类型都会被转换成整型。
编译器隐式执行的任何转换都可由static_cast显示完成。
const_cast转换去掉表达式的常量性。
reinterpret_cast用来在指针之间转换。
C++强制转换符号 type (expr);
C语言强制转换符号 (type) expr;
C++标准中声明也是一条语句,可以出现在if语句的判断条件内,此时它的作用域只在此if语句内。
模板语法:
template <class elemType>
class list;
或
template <typename elemType>
class list;
顺序容器拥有由单一类型元素组成的一个有序集合。主要有list和vector。(deque提供了与vector相同的行为,但是对于首元素的有效插入和删除提供了特殊的支持。)
关联容器支持查询一个元素是否存在,并且可以有效地获取元素。两个基本的容器类型是map和set,它们只包含每个键的唯一出现,multimap和multiset支持同一个键的多次出现。
vector用数组实现,deque用两级数组结构实现,list用双向链表实现。
通过reserve可以显式指定一个容器的容量,但对简单类型往往会引起性能退化。
如果一个C++编译器不支持缺省模板参数,那么它要求第二个实参指定分配器,这时容器声明如下:
vector<string, allocator> svec;
容器的类型必须支持等于和小于操作符,并且有缺省构造函数。
const_iterator对于遍历const容器是必需的。
istream_iterator 输入流迭代器
string类的几个查找函数find rfind find_first_of等。
map定义了一个类型value_type,表示相关联的键值对。
map<string, int> word_count;
int count = word_count[“wrinkles”];
上述代码可能会引起插入一个实例,判断一个键是否存在的方法是使用count或find函数。
map中一个迭代器指向一个pair对象。
typedef set<string>::difference_type diff_type;
istream_iterator<string, diff_type> input_set(infile), eos;
copy(input_set, eos, inserter(exclusion_set, exclusion_set.begin()));
difference_type是字符串set中两个iterator相减的结果类型,copy只能顺序拷贝,inserter类使其能插入每个元素。
对于multimap或multiset,一种迭代策略是联合使用由find返回的iterator和count返回的值,另一种是使用由equal_range返回的iterator对值,它的第一项表示开始位置,第二项标识结束位置。
如果一个参数在函数中可能指向不同的对象,或这个参数可能不指向任何对象,则必须使用指针参数。
为了支持类类型,特别是支持有效直观地实现重载操作符机制,C++特别引入了引用机制。
使用数组引用可以传递数组的长度。
int f(int a, int b, int c = 0);
int f(int a, int b = fun(3), int c);
重新声明时不能给已经有缺省值的参数再指定一个,缺省值可以不是常量表达式。省略号挂起类型检查机制,可以给函数传递任意个任意类型的参数。
在某些情况下,编译器自动将按值返回转换到按引用返回,该优化被称为命名返回值优化。
链接指示符extern “C”告诉编译器后面的语句是用其它的程序设计语言编写的,这是是C。
取函数地址可以加&,也可以不加,相应地通过函数指针调用函数可以加*,也可以不加。
int (*pFun[10])(); //函数指针的数组
参数为函数类型时会自动当成函数指针,而返回函数则会出现编译错误。
int (*ff(int))(int); //返回函数指针的函数
指向C函数的指针和指向C++函数的指针类型不同。链接指示符会影响它作用范围内的所有声明。
if语句的判断条件中声明的变量在其if和else子句中有效。
inline函数和符号常量可以在一个程序中被定义多次,只要他们完全相同。
既指定了关键字extern,又做了初始化,视为定义。
C++中把函数参数的类型和数目编码在函数名中以支持重载和更好的类型检查,该机制叫类型安全链接。
在编译期间,在可能的情况下,符号常量的值会代替该名字的出现,这叫常量折叠。一个具有初始值的常量可以被包含在多个不同的文件中。
当new失败时,会抛出一个bad_alloc异常。
delete会自己判断指针是否为0,我们不需要判断。
auto_ptr可自动释放内存,类似于COM的智能指针,但一块内存只能与一个auto_ptr对象关联,比较操作需要用get函数取得它的指针值,改变它的值需要使用reset函数,release操作返回它拥有的指针并释放拥有权。使用之前需要包含memory文件。
释放动态分配的数组必须加方括号。
const int *pci = new const int(1024);
包含new头文件可使用定位new表达式在已经分配好的内存中创建对象,该内存可以不是动态分配的。
只有当一个名字空间成员在名字空间定义中已经被声明过,它才能在该名字空间定义之外被定义,而且只能在包围该成员声明的名字空间中定义。此时它的返回类型和函数名必须被限定修饰,而参数和定义体内可以使用简短表示。
使用未命名的名字空间可以代替static声明。
using声明在声明出现的域中引入一个名字,using指示符使名字空间成员好像名字空间不存在一样可见。using声明只能引入一个名称,它会隐藏外围域中的名称,只在此域内起作用。using指示符引入所有名称,有冲突的名字只有在被使用时才能检测到。
当一个参数类型是const或volatile时,在识别函数声明是否相同时并不考虑修饰符,但如果把他们应用在指针或引用类型上,则需要考虑
重载函数集合中的全部函数都应该在同一个域中被声明。
链接指示符只能指定重载函数集中的一个函数。
函数重载解析分三步:1.确定重载函数集合和实参属性。2.选出可行函数。3.选择最匹配的函数。
参数匹配分三种情况:精确匹配、与一个类型转换匹配、无匹配。精确匹配可存在如下转换:从左值到右值的转换,从数组到指针的转换,从函数到指针的转换,限定修饰转换。其它转换分成三组:提升、标准转换和用户定义的转换。
限定修饰转换只应用在指针指向的类型上。
标准转换包括:非提升的整型和浮点类型的转换,整数值0到指针类型的转换,其它指针类型(不包括函数指针)到void*的转换,其它类型到bool型的转换。
对于引用参数来说,如果实参是该引用的有效初始值,则该实参是精确匹配,否则不匹配。
如果函数实参的类型是在一个名字空间中被声明的,则该名字空间中与被调用函数同名的成员函数也将被加入到候选函数集中。
一个转换序列潜在地由下列转换以下列顺序构成:左值转换-》提升或标准转换-》限定修饰符转换。如果两个转换序列前面都相同,则没有限定修饰符转换的更匹配。
模板参数可以是一个模板类型参数,它代表了一种类型,也可以是一个模板非类型参数,它代表了一个常量表达式。
告诉编译器一个表达式是类型表达式的机制是在表达式前加上关键字typename。
inline或extern指示符应该放在模板参数表后面。
用函数实参的类型来决定模板实参的类型和值的过程被称为模板实参推演。推演过程中不考虑返回值类型。推演过程中允许三种类型转换:左值转换、限定转换和到一个基类的转换。
我们可以为不能推演出来的模板参数类型显示指定实参类型,跟缺省值一样,我们可以只指定前面的类型。
C++支持两种模板编译模式:包含模式和分离模式。在包含编译模式下,每个模板被实例化的文件中都包含函数模板的定义。在分离模式下,模板定义需要加上关键字export表示可导出的模板。
我们可以用显式实例化声明来控制模板实例化发生的时间,为了避免其它文件中隐式实例化,编译器提供了一个禁止隐式实例化的选项。
模板显式特化:
typedef const char *PCC;
template<> PCC max<PCC>(PCC s1, PCC s2);
template<> PCC max (PCC s1, PCC s2);
在某些情况下,一个函数调用可以由两个不同的函数模板实例化得到,编译器会选择一个最特化的。
函数模板和普通函数同名时,函数重载解析过程为先进行模板实参推演,后进行普通的函数重载解析。普通函数比模板实例化得到的函数优先级更高。
不依赖于模板参数的结构必须在被模板定义使用之前声明,依赖于模板参数的结构必须在实例化之前声明。
函数模板的实例化点总是在名字空间域中,并且跟在引用该实例的函数后。
当某条语句抛出异常时,跟在该语句后面的语句将被跳过。程序执行权被转交给处理异常的catch子句。如果没有catch子句能够处理该异常,则程序执行权又被交给terminate函数。
函数try块把函数体整个包含在一个try语句中。关键字try在函数体的开始花括号之前。
异常对象总是在抛出点被创建。在查找用来处理被抛出异常的catch子句时,因为异常而退出复合语句和函数定义,这个过程被称作栈展开。随着栈的展开,在退出的复合语句和函数定义中的局部变量的生命期也结束了,但它们的析构函数会被调用。
当带有异常规范的函数指针被初始化时,右值的异常规范必须与被初始化的指针一样或更严格。
函数对象是一个类,它重载了调用操作符,使用函数对象可以利用内联编译的好处,还可以缓冲一些数据。
使用预定义的函数对象,需要包含functional头文件,常用的算术、关系和逻辑操作都有定义。
标准库还提供了一组函数适配器,用来特殊化或扩展一元和二元函数对象。它们是一种特殊的类,分为绑定器和取反器:绑定器通过把二元函数对象的一个实参绑定到一个特殊的值上将其转换成一元函数对象;取反器则将函数对象的值翻转。
为了支持用copy算法向容器中插入一组元素,标准库定义了一组插入iterator的适配器函数,它们返回特定的插入iterator。包括back_inserter,front_inserter和inserter。
反向iterator支持反向遍历容器,记为reverse_iterator。
istream_iterator和ostream_iterator支持对输入输出流的操作,应用在此对象上的每个递增操作符,都用输入输出操作符读写一个元素。用一个流对象初始化的流迭代器提供开始位置,用它的缺省构造函数提供结束位置。要使用流迭代器需包含iterator头文件。示例如下:
copy(istream_iterator<int>(cin), istream_iterator<int>(), ostream_iterator<int>(cout));
标准库定义了五种iterator:InputIterator,OutputIterator,ForwardIterator,BidirectionalIterator和RandomAccessIterator。InputIterator需要支持:相等和不相等测试,通过++指向下一个元素,通过*读取一个元素。它不保证支持向窗口的写入操作。OutputIterator与之相反。ForwardIterator用来以某一个遍历方向向容器读或写,BidirectionalIterator支持双向,RandomAccessIterator除了以上内容外,还支持随机读取。
大多泛型算法的前两个实参都是一对iterator,标记操作范围[ first, last )。要求从first通过递增必须能到达last。每个算法的声明指示了他所要求支持的iterator的最小类别。有些算法支持两个版本,一个用内置操作符,一个接受函数指针或函数对象,这种版本有些算法以后缀_if标记。对那些修改所操作容器的算法,加_copy后缀的版本返回一个带有这些变化的容器复本。
1.查找算法共13个,其中lower_bound、upper_bound、equal_range使用二分查找。
adjacent_find:查找第一对相邻的重复元素。返回第一个元素位置,可指定比较方式。ForwardIterator=FI?istream_iterator。
binary_search:二分查找。找到则返回true,可指定排序方式。FI
count,count_if:计数,返回iterator_traits<InputIterator>::distance_type类型,count_if计数指定函数结果为true的次数。InputIterator=II
lower_bound:返回第一个大于等于指定值的元素的位置。FI
upper_bound:返回最后一个大于等于指定值的元素的位置。FI
equal_range:返回等于指定值的元素所在范围。FI
find,find_if:查找第一个满足指定条件的值。II
find_end:在第一个序列中查找第二个序列最后一次出现的位置。FI
find_first_of:在第一个序列中查找第二个序列中任一元素第一次出现的位置。FI
search:在第一个序列中查找第二个序列第一次出现的位置。FI
search_n:查找指定值连续出现N次的第一个位置。FI
2.排序和通用整序算法共14个。稳定算法维持了值相等或同等地满足某个条件的元素的相对关系。
inplace_merge:合并两个排过序的连续序列。BI
merge:合并两个有序序列,返回第三个序列中最后一个元素的下一位置。II、II、OI
nth_element:根据指定元素分组。RI
partial_sort,partial_sort_copy:部分排序。RI/II、RI
partition,stable_partition:分割。BI
random_shuffle:洗牌。RI
reverse,reverse_copy:翻转。BI/BI、OI
rotate,rotate_copy:调换前后两段。FI/FI、OI
sort,stable_sort:排序。RI
3.删除和替换算法共15个。
copy:II、OI
copy_backward:反序拷贝。BI
iter_swap:交换两个迭代器所指的值。FI
remove,remove_if,remove_copy,remove_copy_if:FI/II、OI
replace,replace_if,replace_copy,replace_copy_if:FI/II、OI/FI、OI
swap:交换两个对象。
swap_range:交换两段。FI
unique,unique_copy:将连续的相同的值移开。FI/II、OI
4.排序组合算法。如ABC三个字母组成的序列,六种排列的顺序为ABC、ACB、BAC、BCA、CAB、CBA。
next_permutation:将一个排列重新排列为下一个排列,没有返回false。BI
prev_permutation:将一个排列重新排序为上一个排列,没有返回false。BI
5.算术算法。必须包含numeric头文件。
accumulate:累积,需指定初始值。II
adjacent_difference:创建一个差值序列,第一个元素跟原容器相同。II、OI
inner_product:内积。II
partial_sum:创建一个新的序列,每个元素是原序列中它之前(包括它)所有元素的和。II、OI
6.生成和异变算法
fill,fill_n:把某一段数据都设为指定值。FI
for_each:将某一操作应用在一段数据上。II
generate,generate_n:将某一操作应用在一段数据上。FI/OI
transform:将某一操作应用在一段数据上,结果成为一个新的序列。FI/OI
7.关系算法
equal:将第一个序列的元素比较完为止。II
includes:第一个有序序列的元素是否都包含在第二个有序序列中。II
lexicographical_compare:按字典序第一个序列是否在第二个序列的前面。II
max,min:两个对象比较。
max_element,min_element:整个序列比较。FI
mismatch:将第一个序列比较完为止,找出不匹配的项。II
8.集合算法
set_difference:创建在第一个序列中存在而在第二个序列中不存在的元素的有序序列。II/OI
set_intersection:创建两个容器中元素交集的有序序列。II/OI
set_symmetric_difference:对称差集。
set_union:并集。
9.堆算法。标准库支持最大堆,即一个用数组表示的二叉树,它的每个节点上的键值大于或等于其儿子节点的键值。后三个算法都假定原序列是一个有效的堆。
make_heap:把指定范围内的元素做成一个堆。RI
pop_heap:弹出最大值,也不删除。RI
push_heap:将最后一个元素加入堆。RI
sort_heap:排序。RI
关联容器不允许重新排序,list不能随机访问。为此list为一些算法提供了相应的成员函数。如merge、remove、remove_if、reverse、sort、splice、unique。
类中也可以这样声明两个成员变量:int nWidth, nHeight;
inline函数必须在每个调用它的文件中被定义。
const成员函数可以被相同参数表的非const成员函数重载。
跟const类对象类似,对于一个volatile类对象,只有volatile成员函数、构造函数和析构函数可以被调用。
为了允许修改一个类的数据成员,即使它是一个const对象的数据成员,我们可以把该数据成员声明为mutable。
作为特例,整型的const静态数据成员可以在类体中初始化。此成员是一个常量表达式。如下:
class Account {
static const int nameSize = 16;
};
const int Account::nameSize;
静态数据成员的类型可以是其所属类,可以被作为类成员函数的缺省实参。
int (Account::*pFun)();
(myAccount.*pFun)();
联合是一种特殊的类,它不能有静态数据成员或是引用成员。如果一个类类型定义了构造函数、析构函数或拷贝赋值操作符,则它不能成为union的成员类型。匿名union是没有名字的union,它后面也没有跟着对象定义,它的成员可以在定义它的域中被直接访问。在全局域中定义的匿名union必须被声明在未命名的名字空间中或被声明为static。
位域是一种节省空间的成员,它可以有符号,也可以无符号。
在类定义中用到的名字必须在使用前首先被声明,但内联函数和缺省实参可以例外。如果类成员的定义出现在类体之外,则跟在被定义的成员名后面的程序,直到该成员定义结束,都被认为是在类域之中。
嵌套类也可以被定义在其外围类之外,此时它必须在外围类中先被声明,且外围类中只能声明嵌套类的指针和引用。
只有在名字解析成功之后,编译器才会检查访问许可和类型兼容性。
局部类的成员函数必须被定义在类定义中,但其嵌套类可以在其类定义之外被定义,只要它出现在包含外围局部类定义的局部域内。局部类只能访问在外围局部域中定义的类型名、静态变量以及枚举值。在局部类体内的名字解析过程是:在外围域中查找出现在局部类定义之前的声明。
当只有一个参数时,我们可以用如下方式为构造函数指定实参。
Account myAccount = “bingle”;
缺省情况下,单参数构造函数被用作转换操作符,我们可以使用explicit关键字通知编译器不要提供隐式转换。
非公有的构造函数的主要用处是:1.防止拷贝构造;2.此类只能用作基类。
初始化vector会创建一个临时对象,再在每个元素上依次应用拷贝构造函数。
每个成员在成员初始化列表中只能出现一次,初始化的顺序不是由名字在初始化列表中的顺序决定,而是由成员在类中被声明的顺序决定的。
用一个类对象初始化另一个类对象,被称为缺省的按成员初始化。因为初始化的单元是单个非静态数据成员,而不是对整个类对象的按位拷贝。不允许按成员初始化可以通过把拷贝构造函数定义为私有的或只有声明不提供定义。
C++要求,赋值、下标、调用和成员访问箭头操作符必须被定义为类成员操作符。对称操作符最好定义为名字空间成员。::、.*、.和?:操作符不能被重载。除了对operator()外,对其它重载操作符提供缺省实参都是非法的。
成员访问操作符箭头被重载为一元操作符,它必须返回一个类类型的指针或也重载了箭头操作符的类的对象或引用。
前置的递增和递减操作符被定义为一元操作符,后置的加一个int型参数以示区别。
类成员操作符new()的返回类型必须是void*型,并且有一个size_t类型的参数。delete()的返回类型必须是void,并且第一个参数的类型是void*,还可以有第二个参数,其类型为size_t。它们自动被做成静态成员函数,而无须显式声明。new[]()和delete[]()也一样。我们还可以重载定位new和new[]操作符,如果我们再定义一个参数类型与new()或new[]()匹配的delete()或delete[]()操作符,则编译器在构造函数抛出异常并退出时会自动调用这个操作符来释放存储区。
转换函数必须是成员函数,它的声明不能指定返回类型和参数表,可转换为内置类型、类类型或typedef名,不能转换为数组或函数类型。如果转换的目标与转换函数的类型不完全匹配,且目标类型可以通过标准转换序列到达,则仍可调用转换函数。只带一个参数的构造函数也被作为转换函数,可通过explicit关键字禁止使用此构造函数进行隐式类型转换。
当试图转换一个值时,有可能存在两个不同的用户定义的转换序列,这时同时使用的标准转换将成为判断依据。
如果实参是一个类类型的对象、类类型的指针、类类型的引用或指向类成员的指针,则在该类所在名字空间的同名函数和该类的同名友员函数在函数重载解析过程中也被加入候选函数集中。
在成员函数重载解析过程中,静态函数和普通函数同时被加入候选函数集,通过类名的调用也可能被解析成一个普通函数而不是等级差一点的静态函数。而通过const对象或const指针的调用则只会把const函数和静态函数加入候选函数集。
使用操作符语法并带有类类型操作数的操作符在重载解析过程中除了带有类类型实参的普通函数需要加入的三个候选函数集外,还需要加入左操作数的成员操作符函数集合和内置操作符集合。
类模板的参数可以有缺省实参,这对类型参数和非类型参数都一样。
只有当代码中使用了类模板的一个实例的名字,并且上下文环境要求必须存在类的定义时,这个类模板才被实例化。声明一个类模板实例的指针和引用不会引起类模板实例化。
绑定给非类型参数的表达式必须能在编译时刻被计算出结果。在模板实参的类型和非类型模板参数的类型之间允许进行精确匹配允许的转换、提升和整值转换。
类模板的成员函数本身也是一个模板,它只有在被调用或者取地址时才被实例化。
有三种友元声明可出现在类模板中:1.非模板友员类或友元函数,它们不必事先被声明,但类成员函数必须先定义类。2.绑定的友元类模板或函数模板,它们的声明或定义必须先被给出。3.非绑定的友元类模板或函数模板。
类模板的嵌套类自动成为一个类模板,它也只有在被使用时才会实例化。
任何成员函数都可以被定义为成员模板。
只有当编译器看到了实际的类模板定义,而不仅仅是声明时,它才能实例化类模板。在类模板前加上export关键字可将所有非内联成员函数声明为可导出的。显式实例化类模板时,他的所有成员也被显式实例化。
我们可以显式特化整个类模板,也可以单独显式特化某一个成员。如果整个类被特化了,标记特化定义的符号template<>只能被放在类模板的显式特化的定义之前,类模板特化的成员定义不能以符号template<>打头。当程序声明了类模板部分特化时,编译器选择针对该实例而言最为特化的模板定义进行实例化。
类模板的实例化点总是在名字空间域中,而且它总是在引用类模板实例的声明或定义之前,类模板的成员函数或静态数据成员的实例化点也总是跟在引用类模板成员实例的声明或定义之后。
类模板或类模板成员的特化声明必须被声明在定义通用模板的名字空间中。
可以用using声明把基类中某个名字引入派生类的域以形成重载。基类的静态成员由所有派生类共享。
构造函数调用顺序为:基类构造函数、成员类对象构造函数、派生类构造函数。
第一次引入虚拟函数的基类时,必须在类声明中指定virtual关键字,不能在类外的定义中指定。
派生类实例虚拟函数的返回值可以是基类实例返回类型的公有派生类类型。
我们也可以为线虚函数提供定义,它可以通过类域操作符静态调用。
缺省实参在编译时刻确定。
根基类的析构函数最好声明为公有的虚拟的。
因为new操作符是静态的,通常通过虚拟的clone函数来动态复制对象。
在多继承下,名字解析的查找过程对每个基类的继承子树同时进行检查。
在非虚拟派生中,派生类只能显式初始化其直接基类。而在虚拟派生中,虚拟基类的初始化变成了最终派生类的责任。
虚拟基类总在非虚拟基类之前被构造。在虚拟派生下,对于虚拟基类成员的继承比该成员后来重新定义的实例的权值小。
类模板既可以被用作基类,也可以被用作派生类。模板参数自己也可以被用作基类。
如果一个模板名字被用作类型指示符,则必须用完整的参数表对它进行限定修饰。
dynamic_cast允许把基类指针转换为派生类指针。typeid操作符得到指针或引用指向的对象的实际派生类型。它们的操作数的类型必须是带有一个或多个虚拟函数的类类型。
dynamic_cast在运行时刻执行,如果针对指针类型的dynamic_cast失败,结果为0,如果针对引用类型的dynamic_cast失败,它会抛出一个bad_cast异常。
使用typeid操作符必须包含typeinfo头文件。若它的操作数不包含虚拟函数,则只能得到基类的类型。它返回一个type_info对象,它的name函数可以取得其类型字符串。
异常处理通常在抛出点创建一个临时对象,再用拷贝构造函数创建一个异常对象,销毁临时对象,再查找异常处理代码。rethrow表达式重新抛出异常对象,而不是按值传给catch子句的局部对象。
异常规范跟在函数声明的const或volatile限定修饰符之后。派生类虚拟函数的异常规范必须与基类虚拟函数的异常规范一样或更严格。如果一个异常规范指定了一个类,则该函数可以抛出从该类公有派生的类类型的异常对象(指针也一样)。类的构造函数必须使用函数try块才能把初始化表包含在内。
C++标准库中异常的根类为exception,它包含在exception头文件中。使用预定义的异常类必须包含stdexcept头文件。
如果普通函数调用的实参是类类型的对象、类类型的指针或类类型的引用,则候选函数是以下集合的并集:1.在调用点上可见的函数;2.在“定义该类类型的名字空间”或“定义该类的基类的名字空间”中声明的函数;3.该类或其基类的友元函数。
在多继承下建立候选成员函数集时,成员函数的声明必须在同一个基类中被找到。
对于从派生类类型到不同基类类型的不同标准转换进行等级划分时,对于从派生类到基类类型移动较少的转换,被认为好于移动较多的转换。
iostream库还支持内存输入/输出,分别为istringstream、ostringstream和stringstream,需包含sstream头文件。UNICODE版本都在前面加w。缺省以十六进制显示地址,以0和1显示bool值,我们可以用boolalpha操纵符来改变这种缺省行为。
有两种情况会使一个istream对象被计算为false:读文件结束(cin输入ctrl+z)或遇到一个无效的值。缺省情况下,输入操作符丢弃任何中间空白,可使用get函数或noskipws操纵符来录入空白字符。setw(bufSize)把长度大于等于bufSize的字符串分成最大长度为bufSize-1的两个或多个字符串,在每个新串的末尾放一个空字符,使用它需要包含iomanip头文件。
get(char&ch)读取一个字符,返回istream;get()返回该字符值,结束返回EOF;get(char *sink, streamsize size, char delimiter = ‘/n’)读入最多size – 1个字符,返回istream,它不会读入分隔符,需要用ignore丢弃。getline跟get的第三种形式类似,且自动丢弃分隔符。gcount返回这两个函数实际被读入的字符个数。
read、write、put、putback、unget、peek。
cin.setstate(ios_base::failbit);
seekg、seekp、tellp、tellg;clear、setstate、rdstate、eof、bad、fail、good。
setf、unsetf。
操 作 符 含 义
boolalpha 把true 和false 表示为字符串
*noboolalpha 把true 和false 表示为0 1
showbase 产生前缀指示数值的进制基数
*noshowbase 不产生进制基数前缀
showpoint 总是显示小数点
*noshowpoint 只有当小数部分存在时才显示小数点
Showpos 在非负数值中显示+
*noshowpos 在非负数值中不显示+
*skipws 输入操作符跳过空白字符
noskipws 输入操作符不跳过空白字符
uppercase 在十六进制下显示0X 科学计数法中显示E
*nouppercase 在十六进制下显示0x 科学计数法中显示e
*dec 以十进制显示
hex 以十六进制显示
oct 以八进制显示
left 将填充字符加到数值的右边
right 将填充字符加到数值的左边
Internal 将填充字符加到符号和数值的中间
*fixed 以小数形式显示浮点数
scientific 以科学计数法形式显示浮点数
flush 刷新ostream 缓冲区
ends 插入空字符然后刷新ostream 缓冲区
endl 插入换行符然后刷新ostream 缓冲区
ws 吃掉 空白字符