各章小节
第1章 基本概念
·C++中的程序至少包含一个main()函数。
·函数的可执行部分由包含在一对花括号中的语句组成。
·一对花括号定义了一个语句块。
·在C++中,语句用分号结束。
·关键字是C++中有特殊含义的一组保留字。程序中的实体不能与C++语言中的任何关键字同名。
·C++程序包含在一个或多个文件中。
·定义函数的代码通常存储在扩展名为.cpp的文件中。
·定义数据类型的代码通常存储在扩展名为.h的头文件中。
·标准库提供了支持和扩展C++语言的大量功能。
·C++中的输入和输出是利用流来执行的,并且需要使用插入和提取运算符,即<<和>>。
·面向对象的编程方式需要定义专用于某程序的新数据类型。一旦定义好需要的数据类型,就可以根据这些新数据类型来编写程序。
第2章 基本数据类型和计算
·数值和字符常量称为字面量。
·可以把整数字面量定义为十进制、十六进制或八进制。
·浮点字面量必须包含小数点或指数,或两者都包含。
·C++中的已命名对象,例如变量,其名称可以包含一组字母和数字,但第一个字符必须是字母,下划线也看作是字母。大小定字母是不同的,因此变量是区分大小写的。
·由于以下划线开头后跟一个大写字母的名称,以及包含两个连续下划线的名称,是标准库中使用的保留名称,因此它们不应用作变量名。
·C++中的所有字面量和变量都有给定的类型。
·可以存储整数的基本类型有short、int和long。它们在默认情况下存储带符号的整数,也可以使用类型修饰符unsigned来限定这些类型。
·char类型的变量可以存储单个字符。char类型在默认情况下可以是带符号的,也可以是不带符号的,这取决于编译器。也可以使用signed char和unsigned char类型的变量存储整数。
·浮点数的数据类型有float、double和long double。
·变量的名称和类型出现在声明语句中,以一个分号结束。声明一个变量,如果给该变量分配了内存空间,那么也就定义了该变量。
·变量在声明时可以指定初始值,这是一种很好的编程习惯。
·可以用修饰符const保护基本类型的“变量”值。编译器会在程序源代码文件中检查是否试图修改声明为const的变量。
·lvalue是出现在等号左边的一个对象或表达式,非const的变量就是lvalue。
第3章 处理基本数据类型
·可以在表达式中混合使用不同类型的变量和常量。编译器会在需要时,自动把变量转换为相应的类型。
·当等号右边的类型与等号左边的类型不同时,也可以把等号右边的类型自动转换为等号左边的类型。当左边的类型不能完全包含与右边类型的信息相同的信息时,就可能丢失信息。例如把double转换为int或把long转换为short。
·使用static_cast<>(),可以把一种基本类型的值转换为另一种基本类型。
·在默认情况下,在一个块中声明的变量是自动变量,也就是说,它在声明它的那行代码处开始存在,到包含其声明的块的结尾处消失。块尾用右花括号表示。
·变量可以声明为静态,此时该变量存在于程序的整个生成周期。但是,它只能在定义的作用域中访问。如果没有显示初始化静态变量,它就会默认初始化为0。
·在程序中,变量可以在所有块的外部声明,此时该变量具有全局命名空间作用域,在默认情况下具有静态的存储持续时间。在包含它们的程序文件中,具有 全局作用域的变量可以在声明它之后的任何位置被访问,除了存在一个与该全局变量同名的局部变量之外。即使如此,全局变量仍可以使用作用域解析运算符 (::)访问。
·关键字typedef允许定义其他类型的同义词。
·extern关键字允许引用在另一个文件中定义的全局变量。
第4章 选择与决策
·可以使用比较运算符比较两个值,得到一个bool类型的值,它可以是true或false。
·可以把bool值强制转换为整数类型——true强制转换为1,false强制转换为0。
·可以把数值强制转换为bool类型——0强制转换为false,非0值强制转换为true。
·if语句可以根据条件表达式的值执行一个语句或语句块。如果条件是true或非0值,就执行语句或语句块。如果条件是false,或0,就不执行。
·if-else语句给简单的if语句提供了另一个选项。如果条件为false或0,就执行else语句。
·if和if-else语句可以嵌套。
·cctype头文件提供了标准库函数的声明,这些函数可以为字符分类,把字母转换为大写或小写形式。cwctype头文件提供了这些函数的多字节字符版本的声明。
·switch语句可以根据整数表达式的值,从一组固定的选项中选择。
·条件去处符根据一个表达式的值,选择两个值中的一个。
·使用goto语句,可以无条件地分支带有指定标签的语句。
第5章 循环
·循环是重复执行一组语句块的机制。
·有3种循环:while循环、do-while循环和for循环。
·只要指定的条件为true,就重复执行while循环。
·do-while循环至少执行一次,只要指定的条件为true,就继续执行该循环。
·for循环通常用于重复指定的次数,它有3个控制表达式。第一个是初始化表达式,仅在循环的开始执行一次。第二个是循环条件,在每次迭代之前执行,它必须为true,循环才会继续。第三个控制表达式在每次迭代结束时执行,通常用于递增循环计数器。
·任何类型的循环都可以嵌套在其它类型的循环当中,嵌套次数不限。
·在循环中执行continue语句会跳过当前迭代的剩余语句,如果循环控制条件允许,就直接开始下一次迭代。
·在循环中执行break语句会立即退出循环。
·循环定义了一个作用域,在循环中声明的变量不能在该循环外部访问。特别是,在for循环的初始化表达式中声明的变量不能在循环外部访问。
第6章 数组和字符串
·数组是同一类型的数值的命名集合,它们存储在连续的内存块中,每个值都可以通过一个或多个索引值来访问。
·一维数组需要一个索引值来引用其元素,二维数组需要两个索引值,n维数组需要n个索引值。
·数组的元素可以用在等号的左边和表达式中,其方法与相同类型的变量一样。
·char类型的一维数组可以用于存储非空字符串。
·可以让编译器根据声明语句中初始化值的个数,来决定数组中最左边一维的大小。
·可以把char类型的二维数组用作非空字符串的一维数组。
·string类型存储了一个字符串,它不需要终止字符。
·在string变量名后面的方括号中指定索引值,就可以访问string对象中的各个字符。索引值从0开始。
·string类型的对象可以利用函数来搜索、修改和提取子字符串。
·声明string类型的数组与声明其他类型的数组所采用的方法是一样的。
第7章 指针
·指针是包含地址的变量。
·使用地址运算符&可以获取变量的地址。
·要引用指针指向的值,应使用间接运算符*。它也称为解除引用运算符。
·可以对存储在指针中的地址加减整数值。其结果就像指针引用一个数组一样,指针会变为整数值所指定的数组元素的个数。
·运算符new会分配自由存储区中的一块内存,返回所分配的内存地址,使它可在程序中使用。
·运算符delete可以释放用运算符new分配的内存块。
·reinterpret_cast<>()运算符可以把一种类型的指针转换为另一种类型。
第8章 使用函数编程
·函数是一个自包含的代码单元,它有着已定义好的目的。一般的程序问题包含大量的小函数,而不是包含几个大函数。
·函数定义包含定义了参数和返回类型的函数头,以及包含函数的可执行代码的函数体。
·函数原型允许编译器处理对该函数的调用,但此时函数定义可能还没有处理。
·由于给函数传送参数的按值传送机制,是传送原参数值的副本,因此原参数值不能在函数中访问。
·给函数传送指针可以修改该指针所指向的值,但指针本身是按值传送。
·把指针参数声明为const可以防止修改原来的值。
·为函数的参数指定默认值后,只要参数有默认值,就允许有选择地省略参数。
·从函数中返回引用,允许在等号运算符的左边使用该函数。把返回类型声明为const引用,可以阻止在等号运算符的左边使用该函数。
第9章 函数
·重载函数是名称相同、但参数列表不同的函数。重载函数不能仅通过返回类型来区分。
·函数签名由函数名和指定的参数个数及类型来确定。在调用重载函数时,编译器会检查函数签名,把它与可用的函数作比较,然后选择合适的函数。
·函数模板是自动生成重载函数的一种方法。
·函数模板有一个或多个参数,这些参数通常是类型变量,也可以是非类型的变量。函数模板的实例,即函数定义,由编译器为每个对应于一组唯一模板参数的函数调用创建的。
·函数模板可以用其他函数或函数模板来重载。
·函数指针存储了函数的地址,以及参数的类型和个数、返回类型等信息。
·可以使用函数指针来存储有对应返回类型、参数类型和个数的任一函数地址。
·可以使用函数指针来调用它包含的地址上的函数,还可以把函数指针作为函数参数来传送。
·递归函数是调用它自身的函数。采用递归方式实现算法有时可以得到非常简明的代码,但与实现同一算法的其他方法相比,采用递归方式常常需要更多的执行时间。
第10章 程序文件和预处理器
·头文件包含声明、没有生成可执行指令的其他代码,以及内联函数定义。按照约定,头文件使用扩展名.h。
·把函数定义和逻辑变量放在源文件中,源文件的扩展名是.cpp。
·包含所需声明的头文件的内容应通过#include指令,插入到.cpp文件中。
·.cpp文件构成了一个转化单元,编译器把转化单元作为一个单元来处理,以生成对象文件。
·命名空间定义了一个作用域——在这个作用域内声明的所有名称都附加了命名空间的名称。不在显式命名空间作用域内声明的名称就在全局命名空间中。
·一个命名空间可以由几个独立的同名命名空间声明组成。
·在不同的命名空间中声明的相同名称是不同的。
·为了在命名空间的外部引用在命名空间中声明的标识符,需要指定命名空间的名称和标识符,两者之间用作用域解析运算符::分隔开。
·在某个命名空间中声明的名称,在这个命名空间中使用时,可以不加限定符。
·预处理器执行预处理器指令,在编译代码之前传送转化单元中的源代码。处理完所有的指令后,转化单元就只能包含C++代码,没有预处理器指令了。
·可以使用条件预处理器指令,确保头文件的内容在一个转化单元中没有重复。
·可以使用条件预处理器指令,控制是否在程序中包含跟踪或其他诊断调试代码。
·assert()宏允许在执行过程中测试逻辑条件,如果逻辑条件为假,就输出一个消息,并终止程序。
第11章 创建自己的数据类型
·结构类型是程序中的一个新数据类型。
·结构对象是带有成员的对象,这些成员在默认情况下可公开访问。结构可以有数据成员和函数成员。
·可以使用对象名和句点分隔的成员名来引用结构对象的成员。其中句点称为成员选择运算符。
·联合是一种数据类型,它的对象可以使用同一个内存块在不同的时刻存储几种不同变量的值(也许类型也不同)。
·在声明联合对象时,只能为联合的第一个成员提供对应类型的初始值。
·结构的数据成员可以是任意类型,包括其他结构,但数据成员的类型不能与包含它的结构的类型相同。
·不需要构造函数来创建的结构对象叫做聚合;聚合的成员可以用花括号中的初始值列表来初始化。
第12章 类
·类提供了定义自己的数据类型的一种方式。类可以反映某个问题所需要的对象类型。
·类可以包含数据成员和成员函数。类的成员函数总是可以自由访问该类中的数据成员。
·类的对象用构造函数来创建和初始化。在声明对象时,会自动调用构造函数。
·构造函数可以重载,以提供初始化对象的不同方式。
·类的成员可以指定为public,此时它们可以由程序中的任何函数自由访问。另外,类的成员还可以指定为private,此时它们只能被类的成员函数或友元函数访问。
·类的数据成员可以定义为static。无论类中创建了多少个对象,类中的静态数据成员都只有一个。
·可以在类对象的成员函数中访问类的静态数据成员,它们不是类对象的一部分,类对象的大小不包括静态数据成员的字节数。
·即使没有创建类的对象,类的静态函数成员也存在,并可以调用。
·类的每个非静态函数成员上都包含指针this,它指向调用该函数的当前对象。
·类的静态函数成员不包含指针this。
·类中声明为const的成员函数不能修改类对象的数据成员,除非数据成员声明为mutable。
·把类对象的引用用作函数调用的参数,可以避免产生把复杂对象传送给函数的系统开销。
·副本构造函数可以用类中已有的对象初始化同一个类中的新对象。如果没有定义类的构造函数,编译器就会生成默认的副本构造函数。
第13章 类的操作
·只能通过构造函数的初始化列表来初始化类的引用成员。引用不能用赋值语句来初始化。
·只要给函数按值传送对象,就会调用副本构造函数。其结果是传送给类的副本构造函数的参数必须是一个引用。
·如果在类的成员函数中动态分配内存,就总是要执行析构函数来释放内存、实现副本构造函数和副本赋值运算符。
·把类的所有成员都声明为private,就可以限制对类的访问。此时,只有友元类可以创建该类类型的对象。
·嵌套类是把自己的定义放在另一个类定义的内部。嵌套类的名称在包含类的作用域内。为了在包含类的外部引用嵌套的类类型,类型名称必须用包含类的名称来限定。
·如果嵌套类的定义放在包含类的私有部分,嵌套类类型的对象就不能在包含类的外部创建。
第14章 运算符重载
·在类中可以重载任何运算符,以提供针对该类的功能。但作用域解析运算符、条件运算符、成员访问运算符、解除类成员指针的引用运算符和sizeof运算符不能重载。
·运算符函数可以定义为类的成员或全局运算符函数。
·如果一元运算符定义为类的成员函数,操作数就是类对象。
·如果一元运算符定义为全局运算符函数,操作数就是类对象。
·如果二元运算符定义为类成员函数,左操作数就是类对象,右操作数就是函数的参数。
·如果二元运算符定义为全局运算符函数,第一个参数指定左操作数,第二个参数指定右操作数。
·要重载递增运算符,需要用两个函数分别提供运算符的前缀和后缀形式。实现后缀运算符的函数有一个int类型的额外参数,它仅用于与前缀函数区分。递减运算符也是这样。
·实现+=运算符重载的函数可以用在+函数的重载上。所有op=运算符都是这样。
·智能指针是一个操作类似于指针的对象。智能指针的一种形式是迭代给定类型的对象的复杂集合,采用的方式与一般指针类似。标准模板库扩展使用了这种形式。
第15章 继承
·类可以派生自一个或多个基类,此时派生类在其所有的基类中继承成员。
·单一继承就是从一个基类中派生新类。多重继承就是从两个或多个基类中派生新类。
·访问派生类的继承成员由两个因素控制:基类中成员的访问指定符和在派生类声明中基类的访问指定符。
·派生类的构造函数负责初始化类的所有成员,包括继承的成员。
·创建派生类对象一般需要按顺序(从最一般的基类开始到最特殊的直接基类)调用所有直接和间接基类的构造函数,之后执行派生类的构造函数。
·派生类构造函数可以在初始化列表中显式调用直接基类的构造函数。
·在派生类中声明的成员名,如果与继承的成员名相同,就会遮盖继承的成员。为了访问被遮盖的成员,可以使用作用域解析运算符和类名来限定成员名。
·如果派生类有两个或多个直接基类,就会包含同一个类的两个或多个继承子对象,此时,把重复的类声明为虚基类,就可以避免出现重复。
第16章 虚函数和多态性
·多态性是通过指针或引用调用函数,而且调用是动态解析的,即在程序执行时解析。
·基类中的函数可以声明为virtual。在派生于该基类的所有类中,这会迫使该函数问题虚函数。在通过指针或引用调用虚函数时,函数调用就是动态解析的。函数调用的对象类型将确定所使用的函数版本。
·使用对象和直接成员选择运算符来调用虚函数,该函数调用就是静态解析的,即在编译期间解析。
·如果基类包含虚函数,就应把基类的析构函数声明为virtual。这会确保为动态创建的派生类对象选择正确的析构函数。
·纯虚函数没有定义。基类中的虚函数在函数声明的最后加上=0,就变成了纯虚函数。
·包含一个或多个纯虚函数的类称为抽象类,这种类不能创建对象。在该类的任何派生类中,必须定义所继承的所有纯虚函数。否则,该派生类也是抽象类,也不能创建该类的对象。
·虚函数的默认参数值是静态赋予的,如果虚函数的基类版本有默认参数值,在派生类中指定的默认参数值就会被忽略,因为虚函数调用是动态解析的。
·可以声明类成员的指针。它们可以是数据成员的指针,也可以是函数成员的指针。这类指针要与对象、对象的引用或指针一起使用,来引用成员指针定义的对象的类成员。
第17章 程序错误和异常处理
·异常是用于在程序中警示错误的对象。
·可能抛出异常的代码通常包含在try块中。
·处理在try块中抛出的各种类型异常的代码放在该try块后面的一个或多个catch块中。
·try块及其catch块可以嵌套在另一个try块中。
·参数为基类类型的处理程序可以捕获派生类类型的异常。
·如果异常没有被catch块捕获,就调用terminate()函数,该函数会接着调用abort()。
·标准库定义了一组标准异常。
·异常说明限制了函数可以抛出的异常类型。
·如果函数抛出的异常类型不在该函数的异常说明的允许范围之内,就会调用unexpected()函数。
·可以改变unexpected()函数的默认操作,方法是实现自己的unexpected()处理程序,把该函数指针传送给set_unexpected()函数,以建立该处理程序。
·构造函数的函数try块可以包含初始化列表和构造函数体。
·uncaught_exception()函数允许检测因抛出异常而调用的析构函数。
第18章 类模板
·类模板定义了一系列类类型。
·类模板的实例是根据给定的模板参数集,从该模板中生成的类定义。
·声明类模板类型的对象,就会对类模板进行隐式实例化。
·类模板的显示实例化,会根据类模板的给定参数集定义一个类。
·对应于类模板中类型参数的参数,其类型可以是基本类型、类类型、指针或引用类型。
·非类型参数可以是整型类型、枚举类型、指针或引用。
·类模板的部分说明可以根据原类模板中参数的一组限定子集,定义一个新模板。
·类模板的完全说明可以根据原类模板的所有参数参数,定义一个新模板。
·类模板的友元可以 函数、类、函数模板或类模板。
·普通的类可以把类模板或函数模板声明为友元。
第19章 输入输出操作
·标准库支持字符流的输入输出操作。
·输入和输出的标准流是cin和cout,还有错误流cerr和clog。
·提取和插入运算符提供了格式化的流输入输出操作。
·文件流可以与磁盘上的文件关联起来,进行输入、输出或输入输出。
·文件打开模式决定了是从流中读取数据,还是给流写入数据。
·如果创建了一个文件输出流,并把它与一个文件本身不存在的文件名关联起来,就会创建该文件。
·文件有开头、结尾和当前位置。
可以把文件流的当前位置改为以前记录的某个位置。这个位置与流开关的偏移量为正,与流结尾的偏移量为负,而与流当前位置的领衔量可以为正,也可以为负。
·为了支持类对象的流操作,可以重载插入和提取运算符,使这些运算符函数成为类的友元。
·字符串流类提供了string对象的流输入输出操作。
第20章 标准模板库
·STL通过三类模板提供了功能:容器、迭代器和算法。
·容器提供了存储和组织任意类型的对象的各种方式,但要存储的对象类型必须满足元素的基本要求。
·迭代器是操作方式像指针的对象。迭代器是智能指针的示例。
·迭代器可以访问容器中的对象,或从流中提取对象。
·迭代器成对使用时,可以在序列的半开间隔中定义一组对象。第一个对象包含在间隔中,而最后一个对象不包含在间隔中。
·算法是一般化的标准函数,可以处理迭代器指定的对象集。
·算法独立于容器,但可以通过迭代器应用于任何容器中的对象。