C++学习与总结-高级篇
小结:
- 文件读写和文件位置定位
- try、catch、throw
- 定义新的异常,继承exception
- 栈:在函数内部声明的所有变量都将占用栈内存。
- 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。
- 模板
- 预处理器和宏
- 信号处理
- 多线程
- STL
C++ 文件和流
ofstream 和 fstream 对象都可以用来打开文件进行写操作,如果只需要打开文件进行读操作,则使用 ifstream 对象。
文件操作:打开open,关闭close,写入 << ,读取 >>
文件位置指针
istream 和 ostream 都提供了用于重新定位文件位置指针的成员函数。这些成员函数包括关于 istream 的 seekg(”seek get”)和关于 ostream 的 seekp(”seek put”)。
seekg 和 seekp 的参数通常是一个长整型。第二个参数可以用于指定查找方向。查找方向可以是 ios::beg(默认的,从流的开头开始定位),也可以是 ios::cur(从流的当前位置开始定位),也可以是 ios::end(从流的末尾开始定位)。
文件位置指针是一个整数值,指定了从文件的起始位置到指针所在位置的字节数。
C++ 异常处理
三个关键字:try、catch、throw
定义新的异常
您可以通过继承和重载 exception 类来定义新的异常。
C++ 动态内存
C++ 程序中的内存分为两个部分:
- 栈:在函数内部声明的所有变量都将占用栈内存。
- 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。
在 C++ 中,您可以使用特殊的运算符为给定类型的变量在运行时分配堆内的内存,这会返回所分配的空间地址。这种运算符即 new 运算符。
如果您不再需要动态分配的内存空间,可以使用 delete 运算符,删除之前由 new 运算符分配的内存。
malloc() 函数在 C 语言中就出现了,在 C++ 中仍然存在,但建议尽量不要使用 malloc() 函数。new 与 malloc() 函数相比,其主要的优点是,new 不只是分配了内存,它还创建了对象。
C++ 命名空间
在 C++ 应用程序中。例如,您可能会写一个名为 xyz() 的函数,在另一个可用的库中也存在一个相同的函数 xyz()。这样,编译器就无法判断您所使用的是哪一个 xyz() 函数。
因此,引入了命名空间这个概念,专门用于解决上面的问题,它可作为附加信息来区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。
C++ 模板
模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。
模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。
C++ 预处理器
预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。
所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前。预处理指令不是 C++ 语句,所以它们不会以分号(;)结尾。
我们已经看到,之前所有的实例中都有 #include 指令。这个宏用于把头文件包含到源文件中。
- #define 预处理指令用于创建符号常量。该符号常量通常称为宏,
- 您可以使用 #define 来定义一个带有参数的宏
- 条件编译, 有几个指令可以用来有选择地对部分程序源代码进行编译。这个过程被称为条件编译。
1 | #ifdef NULL |
C++ 中的预定义宏:
__LINE__ |
这会在程序编译时包含当前行号。 |
---|---|
__FILE__ |
这会在程序编译时包含当前文件名。 |
__DATE__ |
这会包含一个形式为 month/day/year 的字符串,它表示把源文件转换为目标代码的日期。 |
__TIME__ |
这会包含一个形式为 hour:minute:second 的字符串,它表示程序被编译的时间。 |
# 和 ## 运算符
# 字符串化的意思,出现在宏定义中的#是把跟在后面的参数转换成一个字符串。
当用作字符串化操作时,# 的主要作用是将宏参数不经扩展地转换成字符串常量。
- 宏定义参数的左右两边的空格会被忽略,参数的各个 Token 之间的多个空格会被转换成一个空格。
- 宏定义参数中含有需要特殊含义字符如”或\时,它们前面会自动被加上转义字符 \。
## 连接符号,把参数连在一起。
将多个 Token 连接成一个 Token。要点:
- 它不能是宏定义中的第一个或最后一个 Token。
- 前后的空格可有可无。
C++ 信号处理
信号是由操作系统传给进程的中断,会提早终止一个程序。在 UNIX、LINUX、Mac OS X 或 Windows 系统上,可以通过按 Ctrl+C 产生中断。
有些信号不能被程序捕获,但是下表所列信号可以在程序中捕获,并可以基于信号采取适当的动作。这些信号是定义在 C++ 头文件
signal() 函数
C++ 信号处理库提供了 signal 函数,用来捕获突发事件。以下是 signal() 函数的语法:
1 | void (*signal (int sig, void (*func)(int)))(int); |
您可以使用函数 raise() 生成信号,该函数带有一个整数信号编号作为参数,语法如下:
1 | int raise (signal sig); |
Sleep 函数
功能:执行挂起一段时间,也就是等待一段时间在继续执行
用法:Sleep(时间)
注意:
- (1)Linux 用 #include 和 sleep(),Windos 用 #include 和 Sleep()。
- (2)Sleep括号里的时间,在windows下是已毫秒为单位,而LInux是已秒为单位。
C++ 多线程
多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。
- 基于进程的多任务处理是程序的并发执行。
基于线程的多任务处理是同一程序的片段的并发执行。
c++ 11 之后有了标准的线程库: #include
C++ Web 编程
公共网关接口(CGI),是一套标准,定义了信息是如何在 Web 服务器和客户端脚本之间进行交换的。
C++ STL 教程
C++ 标准模板库的核心包括以下三个组件:
组件 | 描述 | 例子 |
---|---|---|
容器(Containers) | 容器是用来管理某一类对象的集合。C++ 提供了各种不同类型的容器,比如 deque、list、vector、map 等。 | vector,string,list,set(任意两元素不相等),map,stack,queue,deque(双端队列,动态数组) |
算法(Algorithms) | 算法作用于容器。它们提供了执行各种操作的方式,包括对容器内容执行初始化、排序、搜索和转换等操作。 | |
迭代器(iterators) | 迭代器用于遍历对象集合的元素。这些集合可能是容器,也可能是容器的子集。 |
标准模板库包含了序列容器(sequence containers)与关系容器(associative containers)。
数据容器 | 描述 |
---|---|
序列容器 - 有序集 | |
vector) | 动态数组,兼容C语言数组。vector可以如同数组一样的访问方式,例如使用下标(operator[])运算符,并记得自己的长度信息(size),您也可以使用对象的方式来访问vector(push_back、pop_back)。使用vector可以轻易地定义多维可调整型数组(std::vector<std::vector<…> >)。要使用vector,必须含入vector头文件。vector可在O(1)内完成在末尾插入 / 移除元素,但在vector中间或开头插入/移除元素,则需要消耗O(n)时间。 |
list) | list容器是一个有序(Ordered)的数据结构(循序容器),每个元素中存储着上一个元素和下一个元素的地址(指针),因此是一个双向链接的链表。与vector相比,其元素的访问速度较慢,而在已知元素位置的情况下,插入和删除速度较快。STL容器中唯一支持事务语义。 |
forward_list (单向链表) | list的单链表版,去掉了一些操作。 |
deque (双端队列) | 可看做为能在常量时间内完成向开头或结尾插入或删除元素的vector,但是修改之后,其迭代器的有效性就无法得到保障。 |
array | 只能在初始化时指定大小的数组,可视为内置数组的封装。 |
关联容器 - 无序集 | |
set | 不重复元素的集合。 |
multiset | 跟set具有相同功能,但允许重复的元素。 |
map&action=edit&redlink=1) | 关联数组,每个元素含有两个数据项,map将一个数据项映射到另一个数据项中。 |
multimap | 跟map具有相同功能,但允许重复的键值。 |
unordered_set unordered_multiset unordered_map unordered_multimap | 分别类似于集合、多重集合、映射、多重映射,但使用哈希表实现。它的键(Keys)没有排序(operator<),相反必须存在一个从键类型到size_t的哈希函数、且要求键之间可以判等(operator==)。自C++11起进入语言标准。 |
其他类型的容器 | |
bitset | 存储系列位类似的固定大小的布尔向量。实现按位运算,没有迭代器,不是序列。可视为std::array<bool, N>。若需要改变序列长度,可用std::vector |
valarray | 数值类型的std::vector。牺牲泛型能力而专为数值计算做了优化,例如在数组上的sin操作可对数组内所有数值取正弦。有些实现会对std::valarray应用向量指令等优化手段。 一个观点是里面全是数值类型的valarray才是数学意义上的向量,而可以泛型的vector更该叫array——编程语言中的数组。 |