自己写的C++11 Primer Plus 学习笔记,如有雷同不胜荣幸,如有错误敬请指正
1. 嵌套类: 在另一个类中声明的类
声明位置 | 包含它的类是否可以使用它 | 从包含它的类派生而来的类是否可以使用它 | 在外部是否可以使用 |
---|---|---|---|
私有部分 | 是 | 否 | 否 |
保护部分 | 是 | 是 | 否 |
公有部分 | 是 | 是 | 是,通过类限定符来使用 |
2. 异常
abort()
的典型实现是向标准错误流(即:cerr
使用的错误流)发送消息abnormal program termination
(程序异常终止),然后终止程序(是否刷新问价缓冲区取决于实现)。exit()
刷新文件缓冲区,但不显示消息。
异常处理的三个组成部分:
**异常规范(C++11已摒弃,推荐:关键字 noexcept
指出函数不会引发异常)
double harm(double a) throw(bad_thing); //may throw bad_thing exception
double marm(double) throw(); //doesn't throw an exception
///////////////////////
double marm() noexcept; //marm() doesn't throw an exception
其中的 throw()
就是异常规范,它可能出现在函数原型和函数定义中,可包含类型列表,也可不包含
异常规范作用:
try
块,当然也可以在注释中指出栈解退: 当函数由于异常(不是由于返回)而终止,则程序也将释放栈中的内存,但不会释放栈的第一个返回地址后停止,而是继续释放栈,直到找到一个位于 try 块中的返回地址。随后,控制权将转到块尾的异常处理程序,而不是函数调用后面的第一条语句。(如果栈解退,则引发异常后,对于中间函数调用放在栈中的自动类型对象,其析构函数将不会被调用)
引发异常时,编译器总是创建一个临时拷贝,即使异常规范和 catch 块中指定的是引用
引用作为返回值的原因: 基类引用可以执行派生类对象。假设有一组通过继承关联起来的异常类型,则在异常规范中只需列出一个基类引用,它将与任何派生类对象匹配。
假设有一个异常类层次结构,并要分别处理不同异常类型,则使用基类引用将能够捕获任何异常对象;而使用派生类对象只能捕获它所属类及从这个类派生而来的类对象。
可以使用省略号来表示异常类型,从而捕获异常:
catch(...){ //statements }; //catches any type exception
重新定义:
#include
class bad_hmean : public std::exception
{
public:
const char * what(){return "bad arguments to hmean()";}
...
};
class bad_gmean : public std::exception
{
public:
const char * what(){return "bad arguments to gmean()";}
...
};
try{
...
}catch(std::exception & e)
{
cout << e.what() << endl;
...
}
存在浮点类型可以表示的最小非零值,计算结果比这个值还小时将导致下溢错误;整形和浮点型都可能发生上溢错误,当计算结果超过了某个类型能够表示的最大数量级时,将发生上溢错误。
要捕获所有异常(预期和意外异常):
#include
using namespace std;
void myUnexpected()
{
throw std::bad_exception; //or just throw
}
set_unexcepted(myUnexpected);
double Argh(double,double) throw (out_of_bounds,bad_exception);
...
try{
x = Argh(a,b);
}catch(out_of_bounds & ex)
{
...
}catch(bad_exception & ex)
{
...
}
3. RTTI(运行阶段类型识别:Runtime Type Identification): 旨在为程序在运行阶段确定对象的类型提供一种标准方式。
C++ 支持 RTTI 的元素:
dynamic_cast
运算符将使用一个指向基类的指针来生成一个指向派生类指针;否则,该运算符返回0——空指针typeid
运算符返回一个指出对象的类型的值type_info
结构存储了有关特定类型的信息dynamic_cast运算符: Superb * pm = dynamic_cast
typeid 运算符和 type_info 类:
typeid(Mag) == typeid(*pg)
4. 类型转换运算符
dynamic_cast: dynamic_cast
,该运算符的作用是使得能够在类层次结构中进行向上转换,而不允许其他转换
const_cast: const_cast
,该运算符的作用是改变值为 const
或 volatile
(type-name 与 expression 的类型必须相同)
High bar;
const High * pbar = &bar;
...
High * pb = const_cast (pbar); //valid
const Low * pl = const_cast<const Low *> (pbar); //invalid
static_cast: static_cast
,仅当 type_name 可被隐式转换为 expression 所属的类型或 expression 可被隐式转换为 type_name 所属的类型时,上述转换才是合法的。
High bar;
Low blow;
...
High * pb = static_cast (&blow); //valid upcast
Low * pl = static_cast (&bar); //valid downcast
Pond * pmer = static_cast (&blow); //invalid,Pond unrelated
reinterpret_cast: reinterpret_cast
,用于天生危险的类型转换
struct dat {short a; short b;};
long value = 0xA224B118;
dat * pd = reinterpret_cast (&value);
cout << hex << pd->a; //display first 2 bytes of value
通常,这样的转换适用于依赖实现的底层编程技术,是不可移植的。例如:不同系统在存储多字节整形时,可能以不同的顺序存储其中的字节。
1. string 类
basic_string
的一个 `typedef)
构造函数 | 描述 |
---|---|
string(const cahr * s) | 将 string 对象初始化为 s 指向的 NBTS |
string(size_type n,cahr c) | 创建一个包含 n 个元素的 string 对象,其中每个元素都被初始化为字符 c |
string(const string & str) | 将一个 string 对象初始化为 string 对象 str (复制构造函数) |
string() | 创建一个默认的string对象,长度为0(默认构造函数) |
string(const char * s,size_type n) | 将 string 对象初始化为 s 指向的 NBTS 的前 n 个字符,即使超过了 NBTS 结尾 |
template< class Iter> string(Iter begin,Iter end) | 将 string 对象初始化为区间[begin,end)内的字符,其中begin和end行为就像指针,用于指定位置,范围包括begin在内,但不包括end |
string(const string & str,string size_type pos=0,size_type n=npos) | 将一个string 对象初始化为对象 str 中从位置 pos 开始到结尾的字符,或从位置 pos 开始的 n 个字符 |
string(string && str) noexcept | 它将一个string对象初始化为string对象str,并可能修改str(移动构造函数) |
string(initializer_list il) | 它将一个string对象初始化为初始化列表il中的字符 |
**string 版本的 getline() 函数从输入中读取字符,并将其储存到目标string中,直到发生下列三种情况之一:
eofbit
将被设置,这意味着方法 fail()
和 eof()
都将返回 true;\n
),在这种情况下,将把分界字符从输入流中删除,但不储存它string::npos
(通常是无符号 int 或 无符号 long 的最大值) 和可供分配的内存字节数中较小的一个),在这种情况下,将设置输入流的 failbit
,这意味着方法 fail()
将返回 truefind()
方法
方法原型 | 描述 |
---|---|
size_type find(const string & str,size_type pos=0) const | 从字符串的 pos 位置开始,查找子字符串 str。如果找到,则返回该子字符串首次出现时其首字符的索引;否则,返回 string::npos |
size_type find(const char * s,size_type pos=0)const | 从字符串的 pos 位置开始,查找子字符串 s。如果找到,则返回该子字符串首次出现时其首字符的索引;否则,返回 string::npos |
size_type find(const char * s,size_type pos=0,size_type n) | 从字符串的 pos 位置开始,查找 s 的前 n 个字符组成的子字符串。如果找到,则返回该子字符串首次出现时其首字符的索引;否则,返回 string::npos |
size_type find(char ch,size_type pos=0)const | 从字符串的 pos 位置开始,查找字符 ch。如果找到,则返回该字符首次出现的位置;否则,返回 string::npos |
string 库还提供了相关方法:rfind()
,find_last_of()
,find_first_of()
,find_first_not_of()
,find_last_not_of()
,它们重载函数特征标都与 find()
方法相同。rfind()
方法查找子字符串或字符最后一次出现的位置;find_first_of()
方法在字符串中查找参数中任何一个字符首次出现的位置;find_last_of()
查找最后一次出现的位置;find_first_not_of()
在字符串中查找第一个不包含在参数中的字符
2. 智能指针模板类(智能指针是行为类似于指针的模板类对象)
普通指针过期时,其所占据的内存将被释放,但其所指向的内存将不会被释放(因为其不是对象,无法调用其析构函数来删除其指向的内存)
这三个智能指针模板都定义了类似指针的对象,可以将 new 获得的地址赋给这种对象,其析构函数将使用 delete 来释放内存。因此,如果将 new 返回的地址赋给这些对象,将无需记住稍后释放这些内存:在智能指针过期时,这些内存将自动释放
避免程序删除一个对象两次:
auto_ptr
和 unique_ptr
的策略,但 unique_ptr
的策略更严格shared_ptr
采用的策略。unique_ptr
优于 auto_ptr
:
auto_ptr<string> p1 = new string("auto"); //#1
auto_ptr<string> p2; //#2
p2 = p1; //#3
在语句 #3 中,p2 接管 string 对象的所有权后,p1 的所有权将被剥夺。这样可防止 p1 和 p2 的析构函数试图删除同一个对象;但如果程序随后试图使用 p1,这将是坏事,因为p1 不再指向有效的数据。
unique_ptr<string> p3 = new string("auto"); //#4
unique_ptr<string> p4; //#5
p4 = p3; //#6;
编译器认为语句 #6 非法,避免了 p3 不再指向有效的数据的问题。
程序试图将一个 unique_ptr 赋给另一个时,如果源 unique_ptr 是一个临时右值,编译器允许这样做;如果源 unique_ptr 将存在一段时间,编译器将禁止这样做:
unique_ptr<string> pu1 = new string("hi");
unique_ptr<string> pu2;
pu2 = pu1; //#1 not allowed
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string("you")); //#2 allowed
同时,unique_ptr
还可用于数组的变体
使用 new 分配内存时,才能使用
auto_ptr
与shared_ptr
;不使用 new 或 new[] 分配内存时,不能使用unique_ptr
3. 标准模板库
① 模板类 vector
templateT,class Allocator = allocator<T>>
calss vector{...
如果省略该模板参数的值,则容器模板将默认使用 allocator 类(这个类使用 new 和 delete)
vector 模板使用动态内存分配,因此可以初始化参数来指出需要多少矢量
STL 容器的基本方法:
4. 泛型编程
面向对象编程关注的是编程的数据方面,而泛型编程关注的是算法。它们之间的共同点是抽象和创建可重用代码。泛型编程旨在编写独立于数据类的代码。
迭代器类型:
迭代器功能 | 输入 | 输出 | 正向 | 双向 | 随机访问 |
---|---|---|---|---|---|
解除引用读取 | 有 | 无 | 有 | 有 | 有 |
解除引用写入 | 无 | 有 | 有 | 有 | 有 |
固定和可重复排序 | 无 | 无 | 有 | 有 | 有 |
++i,i++ | 有 | 有 | 有 | 有 | 有 |
–i i– | 无 | 无 | 无 | 有 | 有 |
i[n] | 无 | 无 | 无 | 无 | 有 |
i + n | 无 | 无 | 无 | 无 | 有 |
i - n | 无 | 无 | 无 | 无 | 有 |
i += n | 无 | 无 | 无 | 无 | 有 |
i -= n | 无 | 无 | 无 | 无 | 有 |
表达式 | 返回类型 | 满足正向迭代器要求的任何迭代器 | 编译时间 |
---|---|---|---|
X::iterator | 指向 T 的迭代类型 | 满足正向迭代器要求的任何迭代器 | 编译时间 |
X::value_type | T | T 的类型 | 编译时间 |
X u; | 创建一个名为u的空容器 | 固定 | |
X() | 创建一个匿名的空容器 | 固定 | |
X u(a); | 调用复制构造函数后u == a | 线性 | |
X u = a; | 作用同 X u(a) | 线性 | |
r = a; | X& | 调用赋值运算符后r == a | 线性 |
(&a)->~X() | void | 对容器中的每个元素应用析构函数 | 线性 |
a.begin() | 迭代器 | 返回指向容器第一个元素的迭代器 | 固定 |
a.end() | 迭代器 | 返回超尾值迭代器 | 固定 |
a.size() | 无符号整形 | 返回元素个数,等价于a.end()-a.begin() | 固定 |
a.swap(b) | void | 交换 a 和 b 的内容 | 固定 |
a == b | 可转换为 bool | 如果a和b的长度相同,且a中每个元素都等于(==为真)b中相应的元素,则为真 | 线性 |
a != b | 可转换为bool | 返回 !(a==b) | 线性 |
X u(rv) | 调用移动构造函数后,u 的值与 rv 的原始值相同 | 线性 | |
X u = rv; | 作用同 X u(rv) | ||
a = rv; | X & | 调用移动赋值运算符后,u的值与rv的原始值相同 | 线性 |
a.cbegin() | const_iterator | 返回指向容器第一个元素的 const 迭代器 | 固定 |
a.cend() | const_iterator | 返回超尾值 const 迭代器 | 固定 |
7种序列容器类型:
关联容器(将值与键关联在一起,并使用键来查找值): set,multiset,map,multimap
优点:提供了对元素的快速访问;关联容器通常用于确定数据放置位置的算法,以便能够快速检索信息。
无序关联容器: 也是将值与键关联起来,并使用键来查找值;底层差别在于,关联容器是基于数结构的,而无序关联容器是基于数据结构哈希表的,这旨在提高添加和删除元素的速度以及提高查找算法的效率(unordered_set,unordered_multiset,unordered_map,unordered_multimap)
5. 函数对象(函数符): 是可以以函数方式与 () 结合使用的任意对象,这包括函数名,指向函数的指针,重载了 () 运算符的类对象
函数符概念:
运算符 | 相应的函数符 |
---|---|
+ | plus |
- | minus |
* | multiplies |
/ | divides |
% | modules |
- | negate |
== | equal_to |
!= | not_equal_to |
> | greater |
< | less |
>= | greater_equal |
<= | less_equal |
&& | logical_and |
|| | logical_or |
! | logical_not |
使函数成为自适应的原因是,它携带了标识参数类型和返回类型的 typedef 成员,这些成员分别是 result_type,first_argument_type,second_argument_type。
函数符自适应的意义是:函数适配器对象可以使用函数对象,并认为存在这些 typedef 成员
6. 算法
STL 将算法库分成:
1. C++输入输出概述
输出时,程序首先填满缓冲区,然后把整块数据传输给硬盘,并清空缓冲区,已被下一批输出使用,这被成为刷新缓冲区(C++在用户按下回车键时刷新缓冲区)
在程序中包含 iostream 文件将自动创建 8 个流对象(4 个用于窄字符流,4 个用于宽字符流):
常量 | 含义 |
---|---|
ios_base::boolalpha | 输入和输出 bool 值,可以为 true 或 false |
ios_base::showbase | 对于输出,使用C++基数前缀(0,0x) |
ios_base::showpoint | 显示末尾的小数点 |
ios_base::uppercase | 对于16进制输出,使用大写字母,E表示法 |
ios_base::showpos | 在正数前面加上 + |
第二个参数 | 第一个参数 | 含义 |
---|---|---|
ios_base::basefield | ios_base::dec | 使用基数 10 |
ios_base::oct | 使用基数8 | |
ios_base::hex | 使用基数16 | |
ios_base::floatfield | ios_base::fixed | 使用定点计数法 |
ios_base::scientific | 使用科学计数法 | |
ios_base::adjustfield | ios_base::left | 使用左对齐 |
ios_base::right | 使用右对齐 | |
ios_base::internal | 符号或基数前缀左对齐,值右对齐 |
控制符 | 调用 |
---|---|
boolalpha | setf(ios_base::boolalpha) |
noboolalpha | unsetf(ios_base::noboolalpha) |
showbase | setf(ios_base::showbase) |
noshowbase | unsetf(ios_base::noshowbase) |
showpoint | setf(ios_base::showpoint) |
noshowpoint | unsetf(ios_base::noshowpoint) |
showpos | setf(ios_base::showpos) |
uppercase | setf(ios_base::uppercase) |
internal | setf(ios_base::internal,ios_base::adjustfield) |
left | setf(ios_base::left,ios_base::adjustfield) |
right | setf(ios_base::right,ios_base::adjustfield) |
dec | setf(ios_base::dec,ios_base::adjustfield) |
hex | setf(ios_base::hex,ios_base::adjustfield) |
oct | setf(ios_base::oct,ios_base::adjustfield) |
fixed | setf(ios_base::fixed,ios_base::adjustfield) |
scientific | setf(ios_base::scientific,ios_base::adjustfield) |
**头文件 iomanip
最常用的控制符分别是 setprecision()
,setfill()
,setw()
,它们分别用来设置精度,填充字符和字段宽度。setprecision()
控制符接受一个指定精度的正数参数,setfill()
控制符接受一个指定填充字符的 char 参数,setw()
控制符接受一个制定字符==字段宽度的正数参数
流状态: 由三个 ios_base 元素组成(eofbit,badbit,failbit),其中每个元素都是一位,可以是1(设置)或0(清除)。当 cin 操作到达文件末尾时,它将设置 eofbit;当 cin 操作未能读取到预期的字符时,它将设置 failbit(I/O失败也可能将 failbit 设置为 1);在一些无法诊断的失败破坏流时,badbit 元素将被设置。当全部三个状态位都设置为 0 时,说明一切顺利;程序可以检查流状态,并使用这种信息来决定下一步做什么
成员 | 描述 |
---|---|
eofbit | 如果到达文件尾,则设置为1 |
badbit | 如果流被破坏,则设置为1 |
failbit | 如果输入操作未能读取预期的字符或输出操作没有写入预期的字符,则设置为1 |
goodbit | 另一种表示 0 的方法 |
good() | 如果流可以使用(所有位都被清除),则返回true |
eof() | 如果 eofbit 被设置,则返回 true |
bad() | 如果 badbit 被设置,则返回true |
fail() | 如果 badbit或failbit 被设置,则返回true |
rdstate() | 返回流状态 |
exceptions() | 返回一个位掩码,指出哪些标记导致异常被引发 |
exceptions(isostate ex) | 设置哪些状态将导致clear()引发异常 |
clear(iostate s) | 将流状态设置为 s ;s 的默认值为0(goodbit);如果(restate()&exceptions())!=0,则引发异常 basic_ios::failure |
setstate(iostate s) | 调用 clear(rdstate()|s)。这将设置与s中设置的位对应的流状态位,其他流状态位保持不变 |
(clear()
与 setstate()
都重置状态,但 clear()
将状态设置为它是参数,而setstate()
方法只是影响其参数中已设置的位)
exceptions() 方法返回一个位字段,它包含三位,分别对应 eofbit,badbit,failbit。修改流状态涉及 clear(),setstate(),这都将使用 clear()。修改流状态后,clear()方法将当前的流状态与exceptions() 返回的值进行比较。如果在返回值中某一位被设置,而当前状态中的对应位也被设置,则 clear() 将引发 ios_base::failure 异常。
特征 | cin.get(ch) | cin.get() |
---|---|---|
传输输入字符的方法 | 赋给参数ch | 将函数返回值赋给ch |
字符输入时函数的返回值 | 指向 istream 对象的引用 | 字符编码(int 值) |
达到文件尾时函数的返回值 | 转换为 false | EOF |
方法 | 行为 |
---|---|
getline(char *,int) | 如果没有读取任何字符(但换行符被视为读取了一个字符),则设置 failbit;如果读取了最大数目的字符,且行中还有其他字符,则设置 failbit |
get(char *,int) | 如果没有读取任何字符,则设置 failbit |
peek() 函数返回输入中的下一个字符,但不抽取输入流中的字符
gcount() 方法返回最后一个非格式化抽取方法读取的字符数
putback() 函数将一个字符插入到输入字符中,被插入的字符将是下一条输入语句读取的第一个字符|
2. 文件输入和输出
以默认模式打开文件进行输入将自动把文件的长度截断为零,这相当于清除已有的内容。
读取文件的要求与写入文件相识:
常量 | 含义 |
---|---|
ios_base::in | 打开文件,以便读取 |
ios_base::out | 打开文件,以便写入 |
ios_base::ate | 打开文件,并移到文件尾 |
ios_base::app | 追加到文件尾 |
ios_base::trunc | 如果文件存在,则截短文件 |
ios_base::binary | 二进制文件 |
C++模式 | C模式 | 含义 |
---|---|---|
ios_base::in | “r” | 打开以读取 |
ios_base::out | “w” | 等价于 ios_base::out | ios_base::trunc |
ios_base::out | ios_base::trunc | “w” | 打开以写入,如果已经存在,则截短文件 |
ios_base::out | ios_base::app | “a” | 打开以写入,只追加 |
ios_base::out | ios_base::out | “r+” | 打开以读写,在文件允许的位置写入 |
ios_base::out | ios_base::out | ios_base::trunc | “w+” | 打开以读写,如果已经存在,则首先截短文件 |
c++mode | ios_base::binary | “cmodeb” | 以 C++mode(或相应的cmode)和二进制模式打开;例如:ios_base::in | ios_base::binary 成为 “rb” |
c++mode | ios_base::ate | “cmode” | 以指定的模式打开,并移到文件尾。C 使用一个独立的函数调用,而不是模式编码 |
3. 内核格式化: 读取 string 对象中的格式化信息或将格式化信息写入 string 对象中。