重温C++primer,又有不同的感受,随手把里面的一些知识点记录下来,以供以后参考:
声明declaration 与定义:
declaration :在计算机内存中定义一块内存区域,并用对应的变量与它相联系起来,然后付给它一初始值。
定义:指定了变量的类型和标识符,但它并不提供初始值。
预处理器指示符:
#include 指示符读入指定文件的内容它有两种格式
#include <some_file.h>
#include "my_file.h"
如果文件名用尖括号< 和> 括起来表明这个文件是一个工程或标准头文件,查找过程会检查预定义的目录。我们可以通过设置搜索路径、环境变量或命令行选项来修改这些目录,在不同的平台上这些方法大不相同,建议你请教同事或查阅编译器手册以获得更进一步的信息;如果文件名用一对引号括起来,则表明该文件是用户提供的头文件,查找该文件时将从当前文件目录开始。
防止重复编译:
由于嵌套包含文件的原因,一个头文件可能会被多次包含在一个源文件中,条件指示符可防止这种头文件的重复处理,例如:
#ifndef BOOKSTORE_H
#define BOOKSTORE_H
/* Bookstore.h 的内容 */
#endif
C代码在C++中的编译:
编译C++程序时编译器自动定义了一个预处理器名字__cplusplus 注意前面有两个下划线因此我们可以根据它来判断该程序是否是C++程序以便有条件地包含一些代码,例如:
#ifdef __cplusplus
// 不错我们要编译C++
extern "C"
#endif
int min( int, int );
在编译标准C 时编译器将自动定义名字__STDC__ 当然__cplusplus 与__STDC__不会同时被定义。
预定义名字:
_LINE__和__FILE__: __LINE__记录文件已经被编译的行数__FILE__包含正在被编译的文件的名字。
编译时间__TIME__ 和日期__DATE__:
assert():assert()是C 语台标准库中提供的一个通用预处理器宏在代码中常利用assert()来判断一个必需的前提条件以便程序能够正确执行。assert()在assert.h中定义。assert.h 是C 库头文件的C 名字C++程序可以通过C 库的C 名字或C++名字来使用它,这个头文件的C++名字是cassert C。
静态与动态内存分配的两个主要区别:
1.静态对象是有名字的变量,我们直接对其进行操作,而动态对象是没有名字的变量,我们通过指针间接地对它进行操作。
2.静态对象的分配与释放由编译器自动处理,程序员需要理解这一点但不需要做任何事情。相反动态对象的分配与释放必须由程序员显式地管理,相对来说比较容易出错,它通过new 和delete 两个表达式来完成。
delete 的使用:
单一对象的delete 表达式形式,如下:
// 删除单个对象
delete pint;
数组形式的delete 表达式如下:
// 删除一个对象数组
delete [] pia;
Class的 关键字private、public和保护protected :
关键字private 和public 控制对类成员的访问出现在类体中公有public 部分的成员在一般程序的任何地方都可以访问它们出现在私有private 部分的成员只能在该类的成员函数或友元friend 中被访问我们要到15.2 节才会解释友元
一般来说公有成员提供了该类的公有接口public interface ——即实现了这个类的行为的操作集合它包括该类的所有成员函数或者只包括其中一个子集私有成员提供私有实现代码private implementation ——即存储信息的数据这种类的公共接口与私有实现代码的分离被称为信息隐藏information hiding信息隐藏是软件工程中一个非常重要的概念。它为程序提供了两个主要好处:
1 如果类的私有实现代码需要修改或扩展那么只有相对很小一部分要求访问这些实现代码的成员函数需要修改而许多使用该类的用户程序无需修改但是要求重新编译。
2 如果类的私有实现代码有错误那么通常需要检查的代码数量只局限在相对较少的需要访问这些实现代码的成员函数上而无需检查整个程序
保护protected 级别在类的保护区
域内的数据成员和成员函数不提供给一般的程序只提供给派生类放在基类的私有区
域内的成员只能供该类自己使用派生类不能使用
函数重载function overloading
函数重载function overloading 的机制函数重载允许两个或更多个函数使用同一个名字限制条件是它们的参数表必须不同参数类型不同或参数的数目不同根据不同的参数表编译器就能够判断出对某个特定的调用应该选择哪一个版本的重载函数。重载函数在运行时刻的行为与非重载函数完全一样主要的负担是在编译时刻用来决定中该调用哪个实例所需要的时间。
引用reference:
引用reference 即IntArray &rhs 引用是一种没有指针语法的指针,与指针一样引用提供对对象的间接访问。在实际的程序中引用主要被用作函数的形式参数——通常将类对象传递给一个函数。
1、引用类型由类型标识符和一个取地址操作符来定义引用必须被初始化。
2、一旦引用已经定义它就不能再指向其他的对象。
3、引用的所有操作实际上都被应用在它所指的对象身上包括取地址操作符。
4、每个引用的定义必须以取地址操作符开始。
5、const 引用可以用不同类型的对象初始化只要能从一种类型转换到另一种类型即可也可以是不可寻址的值如文字常量。同样的初始化对于非const 引用是不合法的将导致编译错误,因为:引用在内部存放的是一个对象的地址它是该对象的别名对于不可寻址的值如文字常量以及不同类型的对象编译器为了实现引用必须生成一个临时对象引用实际上指向该对象但用户不能访问它。如:我们希望用一个const 对象的地址来初始化一个引用非const 引用定义是非法的将导致编译时刻错误
const int ival = 1024;
// 错误: 要求一个const 引用
int *&pi_ref = &ival;
指针:
指针持有另一个对象的地址,使我们能够间接地操作这个对象。每个指针都有一个相关的类型不同数据类型的指针之间的区别不是在指针的表示上也不在指针所持有的值地址上——对所有类型的指针这两方面都是相同的8不同之处在于指针所指的对象的类型上指针的类型可以指示编译器怎样解释特定地址上内存的内容以及该内存区域应该跨越多少内存单元。
1、当指针持有0 值时表明它没有指向任何对象或持有一个同类型的数据对象的地址。
2、指针不能持有非地址值
3、指针不能被初始化或赋值为其他类型对象的地址值,C++提供了一种特殊的指针类型来支持这种需求空void* 类型指针它可以被任何数据指针类型的地址值赋值。
4、指针可以让它的地址值增加或减少一个整数值这类指针操作被称为指针的算术运算pointer arithmetic
指针和引用有两个主要区别:
1、引用必须总是指向一个对象。
2、如果用一个引用给另一个引用赋值那么改变的是被引用的对象而不是引用本身。
异常处理exception handling):
异常处理机制的主要构成如下
1 程序中异常出现的点一旦识别出程序异常就会导致抛出raise 或throw 异常与异常被抛出时正常的程序就被挂起直到异常被处理完毕在C++中异常的抛出由throw表达式来执行,如:
if ( ! infile ) {
string errMsg( "unable to open file: " );
errMsg += fileName;
throw errMsg;
}
2 程序中异常被处理的点典型地程序异常的抛出与处理位于独立的函数或成员函数调用中找到处理代码通常要涉及到展开程序调用栈Program call stack 一旦异常被处理完毕就恢复正常的程序执行但不是在发生异常的地方恢复执行过程而是在处理异常的地方恢复执行过程C++中异常的处理由catch 子句来执行例如下面的catch 子句处理在第1 项中被抛出的异常
catch( string exceptionMsg ) {
log_message( exceptionMsg );
return false;
}
内置数据类型长度:
字符型char 通常用来表示单个字符和小整数它可以用一个机器字节来表示
整型int 短整型short 长整型long 它们分别代表不同长度的整数值典型情况下short 以半个字表示int 以一个机器字表示而long 为一个或两个机器字在32 位机器中int 和long 通常长度相同
浮点型float 双精度double 和长双精度long double 分别表示单精度浮点数双精度浮点数和扩展精度的浮点数值典型情况下float 为一个字double 是两个字long double 为三个或四个字
const 限定修饰符:
volatile 限定修饰符:
当一个对象的值可能会在编译器的控制或监测之外被改变时例如一个被系统时钟更新的变量那么该对象应该声明成volatile 因此编译器执行的某些例行优化行为不能应用在已指定为volatile 的对象上。volatile 修饰符的主要目的是提示编译器该对象的值可能在编译器未监测到的情况下被改变因此编译器不能武断地对引用这些对象的代码作优化处理。
类:
类的定义由关键字class 开始后面是一个标识符该标识符也被用作类的类型指示符如complex vector 及Array 等等一般地一个类包括公有的public 操作部分和私有的private 数据部分这些操作被称为该类的成员函数member function 或方法method它们定义了类的公有接口public interface ——即用户可以在该类对象上执行的操作的集合。
sizeof 操作符:
siseof 操作符的作用是返回一个对象或类型名的字节长度它有以下三种形式
sizeof (type name );
sizeof ( object );
sizeof object;
返回值的类型是size_t 这是一种与机器相关的typedef 定义我们可以在cstddef 头文件中找到它的定义。
1、当sizeof 操作符应用在数组上时例如上面例子中的ia 它返回整个数组的字节长度而不是第一个元素的长度也不是ia 包含的元素的个数。
2、应用在指针类型上的sizeof 操作符返回的是包含该类型地址所需的内存长度但是应用在引用类型上的sizeof 操作符返回的是包含被引用对象所需的内存长度
3、sizeof 操作符在编译时刻计算因此被看作是常量表达式它可以用在任何需要常量表达式的地方。
new 和delete:
系统为每个程序都提供了一个在程序执行时可用的内存池这个可用内存池被称为程序的空闲存储区free store 或堆heap 运行时刻的内存分配被称为动态内存分配dynamic memory allocation。动态内存分配由new 表达式应用在一
个类型指示符specifier 上来完成类型指示符可以是内置类型或用户定义类型new 表达式返回指向新分配的对象的指针。
所有从空闲存储区分配的对象都是未命名的这是它的另一个特点new 表达式并不返回实际被分配的对象而且返回这个对象的地址对象的所有操作都通过这个地址间接来完成。
当对象完成了使命时我们必须显式地把对象的内存返还给空闲存储区我们通过把delete 表达式应用在指向我们用new 表达式分配的对象指针上来做到这一点delete 表达式不应该被应用在不是通过new 表达式分配的指针上。
隐式类型转换:
C++定义了一组内置类型对象之间的标准转换在必要时它们被编译器隐式地应用到对象上隐式类型转换发生在下列这些典型的情况下。
1、在混合类型的算术表达式中在这种情况下最宽的数据类型成为目标转换类型这也被称为算术转换arithmetic conversion。
2、用一种类型的表达式赋值给另一种类型的对象在这种情况下目标转换类型是被赋值对象的类型。
3、把一个表达式传递给一个函数调用表达式的类型与形式参数的类型不相同在这种情况下目标转换类型是形式参数的类型。
4、从一个函数返回一个表达式表达式的类型与返回类型不相同在这种情况下目标转换类型是函数的返回类型。
显式类型转换:
显式转换也被称为强制类型转换cast 包括下列命名的强制类型转换操作符static_cast dynamic_cast const_cast 和reinterpret_cast。
语句:
对于类对象的定义来说由于类对象与构造函数和析构函数相关联所以声明的局部性就变成必需的了当把这些对象放在函数或语句块的开始时发生下面两件事情1 在做函数或语句块中的任何事情之前所有类对象的构造函数均被调用声明的局部性使我们能够把初始化的开销分摊到函数或语句块中2 或许更重要的是通常情况下在函数或语句块内部的所有程序语句都被执行之前该函数或者语句块就结束了
参数传递:
1、按值传递:
C++中参数传递的缺省初始化方法是把实参的值拷贝到参数的存储区中。按值传递时函数不会访问当前调用的实参函数处理的值是它本地的拷贝这些拷贝被存储在运行栈中因此改变这些值不会影响实参的值一旦函数结束了函数的活动记录。将从栈中弹出这些局部值也就消失了在按值传递的情况下实参的内容没有被改变这意味着程序员在函数调用时无需保存和恢复实参的值。
按值传递并不是在所有的情况下都适合不适合的情况包括:
1)、当大型的类对象必须作为参数传递时对实际的应用程序而言分配对象并拷贝到栈中的时间和空间开销往往过大。
2)、当实参的值必须被修改时。
2、引用参数:
把参数声明成引用实际上改变了缺省的按值传递参数的传递机制在按值传递时函数操纵的是实参的本地拷贝当参数是引用时函数接收的是实参的左值而不是值的拷贝这意味着函数知道实参在内存中的位置因而能够改变它的值或取它的地址。
什么时候将一个参数指定为引用比较合适呢?它必须将一个参数改变成指针来允许改变实参的值时就比较合适引用参数的第二种普遍用法是向主调函数返回额外的结果第三种用法是向函数传递大型类对象。
引用和指针参数的关系:
1、引用必须被初始化为指向一个对象一旦初始化了它就不能再指向其他对象指针可以指向一系列不同的对象也可以什么都不指向因为指针可能指向一个对象或没有任何对象所以函数在确定指针实际指向一个有效的对象之前不能安全地解引用dereference 一个指针。
2、对于引用参数函数不需要保证它指向一个对象引用必须指向一个对象甚至在我们不希望这样时也是如此。
3、如果一个参数可能在函数中指向不同的对象或者这个参数可能不指向任何对象则必须使用指针参数。
4、引用参数的一个重要用法是它允许我们在有效地实现重载操作符的同时还能保证用法的直观性。