第六章 语句
1. if (int i = 3) // i转化成bool值true; 迄今为止,在所有用过的类型中,IO类型可以用作条件,vector和string类型一般不可用作条件
上面,出了if后,i不可访问,下面出了while之后同样不可访问
while (int i = fun(j)) // 每次循环要执行i的创建和撤销过程
2. 每个case不一定非要另起一行
case 1: case 2: case 3: case 4: case 5:
++i;
break;
case值必须是整型常量表达式
有两个case值相同则编译错
良好的程序要定义default: 即使没有什么要做的,;空语句也行
switch内部的变量定义:规定只能在最后一个case之后或default之后定义变量,要为某个case定义变量,那么要加{}。这样是为保证这个变量在使用前被定义一初始化(若直接执行后面case,且用到了这个变量 ,由于作用域可见,但未定义就使用了)
3. t6_12.cpp 寻找出现次数最多的单词,要求程序从一个文件中读入
4. do{}while();以;结束
5. 在设计良好的程序中, 异常是程序错误处理的一部分
异常是运行时出现的异常
#include<stdexcept>
try {
if (true)
throwruntime_error(“error!”);
} catch (runtime_error err) {
cout << err.what() << endl;
}
先在当前代码中找catch,若没有匹配的,则一上层调用函数中寻找,若仍没找到,则一直找下去,最终由#include <exception>中定义的terminate终止程序执行
若程序在没有try catch的地方出现异常,则由terminate自动终止程序的执行
bitset的to_ulong操作,若提供的位数大于unsigned long的长度32位时,就异常overflow_error,查看t6_23
bitset<33> ib;
ib[ib.size()-1] = 1; // 最高位是1
try{
ib.to_ulong();
}catch (overflow_error err) {
cout<< "overflow" << endl;
cout<< err.what() << endl; // what返回const chat*
}catch (runtime_error err) {
cout<< "runtimeerror" << endl;
cout << err.what() << endl;
}
6. 异常类 #include <stdexcept>
exception // 最常见异常类
runtime_error // 下面几个运行时错
range_error // 生成的结果超出了有意义的值域范围
overflow_error // 上溢
underflow_error // 下溢
logic_error // 可在运行前检测到的问题,下面几个逻辑错
domain_error // 参数的结果值不存在
invalid_argument // 不合适的参数
length_error //超出该类型最大长度
out_of_range //使用一个超出有效范围的值
#include <new>
bad_alloc
#include <type_info>
bad_cast
以上红色的三个只有默认构造函数,不使用string初始化
7. 使用预处理器编写调试代码
#ifndef NDEBUG
cerr << “oh,shit!” << endl;
#endif
很多编译器执行时
$ CC –DNDEBUG main.C
相当于在main.c开头提供#define NDEBUG预处理命令
8. 预处理宏assert #include <cassert>
assert(expr)与NDEBUG配合使用的
只要NDEBUG没有定义,则执行assert,若为false则输出信息并终止程序的执行
测试不可能发生的条件
异常是处理预期要发生的错误,而断言是测试不可能发生的事情
一旦开发和测试完成,程序就已经建立好,并定义了NDEBUG。成品代码中,assert不执行,因此没有运行时代价,没有运行时检查。
assert仅用于检查确定不可能的条件,只对程序的调试有帮助,不能用来代替运行时的逻辑检查logic_error,也不能代替对程序可能产生的错误runtime_error的检查
9. 百度百科中:
编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作是异常处理的一种高级形式。断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真。
使用断言可以创建更稳定,品质更好且易于除错的代码。当需要在一个值为FALSE时中断当前操作的话,可以使用断言。单元测试必须使用断言
除了类型检查和单元测试外,断言还提供了一种确定各种特性是否在程序中得到维护的极好的方法。
使用断言使我们向按契约式设计更近了一步。
我这样理解,一座桥,有护栏,人们正常行走在上面,
assert(有个人翻越护栏跳进河)
assert(桥断了) // 这些都是我们认为不可能的,假设真的
上面是不可能发生的情况;
try {
if (护栏坏掉)
} catch(runtime_error err) {
cout << err.what() << endl;
}
while(cin >> s) {
assert(cin); // 多余的
}
string s;
while(cin >> s && s != sought) {}
assert(cin);
解释:在打开调试器的情况下,该循环从标准输入读入一系列单词,直到读入的单词与sought相等或遇到文件结束符。如果遇到EOF,则执行assert并终止程序。
如果关闭调试器,则assert不做任何操作
第七章 函数
1. 最大公约数
int a,b;
while (b) {
inttemp = b;
b =a % b;
a =temp;
}
return a;
2. 函数必须指定返回类型
3. 指针形参,不改变实参指针值,但可以改变它们共同指向的对象
4. 形参使用引用可以避免复制。有些类不能复制。应该将不需要修改的引用形参定义为const引用。普通的非const引用形参不能用const对象初始化,也不能用字面值或产生右值的表达式,以及需要进行类型转换的对象作为实参初始化,只能与完全同类型(short给int引用会出错)的非const对象关联!
5. void ptrswap(int *&p1, int*&p2) // 传值(指针值)调用,函数里面交换两个指针的值,这样的话,实参指针也交换了值
6. 什么时候应该将形参定义为引用:修改实参,大型对象不能复制,无法复制的类
7. 通常函数不应该有vector或其他容器类型的形参,传递迭代器就可以了(模板实现)
8. 数组形参
由于不能复制数组,所以无法编写使用数组类型形参的函数。数组可以自动转化成指针 ,所以通常通过操纵指向数组中的元素的指针来处理数组。
以下三个等价:都看作是指向数组元素的指针
void fun(int*)// 最好
void fun(int[]) // ok
void fun(int[10])
数组实参
数组实参若以非引用形式传递,则会悄悄的转成指针,非引用类型的形参会初始化为相应的的副本,实参是第一个元素的地址,而形参复制的是这个地址的副本,操作的是这个副本,所以不会修改实参指针的值,但是可以修改它们共同指向的对象内容。
所以,数组非引用实参是指针的值传递!
如果不修改数组元素(保护数组)时,那么应该定义为指向const对象的指针
void fun(const int*)
数组引用形参
void printValues(int (&arr)[10]) { ...... } // 括号是必须的
这个时候,数组实参不会转成指针了,而是传递数组引用本身
必须写大小,实参的大小必须与10匹配
t16_16,模板实现,允许传递指向任意大小的引用形参
多维数组的传递
void printValues(int (*matrix)[23], int rowSize) { .... } void printValues(int matrix[][23], introwSize); // 实际上是指针
9. t7_15
#define NDEBUG // 必须放在第一行
#include <iostream>
using namespace std;
//#define NDEBUG // 移到第一行去
int main(int argc, char** argv) {
assert(argc== 33);
cout<< "sum=" << (atof(argv[1]) + atof(argv[2])) <<endl;
system("pause");
return0;
}
10. 非void函数必须返回的特殊情况,main函数最后的reutn 0不是必须的
main函数不能递归,不能调用自身
main函数不能重载
#include<cstdlib>定义了两个预处理器变量,说明程序执行成功或失败:
EXIT_FAILURE
EXIT_SUCCESS
11. 函数返回值
返回值用于初始化在调用函数处创建的临时对象temporary object
如果返回非引用类型,使用局部对象来初始化临时对象
如果返回引用类型,则不要返回局部对象的引用,
也不要返回局部对象的指针
返回引用的函数:没有复制返回值,返回的是对象本身
const string &shorterStr(const string&, conststring&);
如果不希望返回的对象被修改,加const(不能作为左值了)
引用返回左值:返回引用的函数返回一个左值
inlinensaddr_t& pkt_src() { return pkt_src_; }
main函数中:
pkt_src()= 实参值;
如果不希望引用值被修改,返回值应该声明为const:
inline cosnt nsaddr_t& pkt_src() { return pkt_src_; }
该引用是被返回元素的同义词
12. 考题
int &max(int a, int b) { // 返回引用.cpp
returna > b ? a : b;
}
int main() {
inti = 1, j = 2;
max(i,j)= 3;
cout<< "i=" << i << ",j=" << j <<endl; // i=1,j=2
// 因为形参不是引用,不会改变j的值,改变的是局部b的值
system("pause");
return0;
}
13. 函数声明
变量和函数都声明在头文件中,定义在源文件中
源文件包含声明函数的头文件,进行匹配检查
声明在头文件中的好处是确保在所有文件中声明一致,修改方便
14. 默认实参,:如果有一个形参有默认实参,那么它后面的所有形参都要有
使用最频繁使用的排在最后
默认实参可以是表达式或有返回值的函数调用
在头文件中声明默认实参(别人包含后才是有效的),在源文件中定义的时候就不要写默认实参了,否则出错,因规定一个文件中默认实参只能声明一次(定义也是声明呀)
15. 自动对象
只有当定义它的函数调用时才存在的对象
一般指形参和局部变量,它们在定义它们的语句块结束时撤销
静态局部对象
在第一次经过它的定义语句时创建,程序结束时撤销
size_t count() {
static size_t c = 0; // 只执行一次
return++c;
}
int main() {
for(size_t ix = 0; ix != 20; ++ix) {
cout<< count() << “ ”;
}
return 0;
}
输出1,2,3,4,5.....
16. const string &shorterStr(const string&,const string&);
定义它的好处:易读,易改,保证统一,code reuse
坏处,慢!怎么办 inline!!!!
对于内联函数,编译器要展开代码,仅看到原型是不够的,
内联函数应该在头文件中定义,在头文件中加入或修改内联函数时,使用该头文件的所有源文件必须重新编译!
17. 成员函数
每个成员函数都有一个this形参,是调用该函数的对象的地址
对象实例调用自己的成员函数,将自己传给这个成员函数与之进行绑定
this指针是隐含的,不能明式出现在形参表中
常成员函数bool same_isbn(constSales_item&)const;
total.same_isbn(trans);调用时,则这个this是指向total的const Sales_item*类型的指针, 是个指向const对象的指针,所以这个函数不能改变数据成员
const对象、指向const对象的指针或引用只能用于调用其const成员函数!!反过来,非const的这帮东西可以调用const的成员函数(并非必须是const的对象才能调用)
换句话:const对象不能调用非const成员函数
对于成员函数,声明与定义必须一致,如果要const那么两者都要同时有const
18. 对象如果在全局作用域或定义为局部静态对象,那么成员初始化为0,如果对象在局部作用域中定义那么没有初始化
如果类成员有内置类型或复合类型(用其他定义的类型,如引用,指针,数组,容器)成员,那么最好自己定义默认构造函数,不要合成的
19. 作业t7_31 Sales_item.h
20. 函数返回类型,最右边形参具有默认实参,以及const的非引用形参不能作为重载的区别标志
int fuc1(int*);
int fuc1(const int*); //重复声明 ,没有error
//对于第二个,实参即可以是const int*也可以是非const int*的普通指针,所以没区别,都是值传递,对副本操作
补充,若形参是const &int那么区别了,是重载!此时,若实参是const对象则调用带有const的那个函数,若调用另一个是需要转换的故而不优,由此区分!若实参是非const对象,则两个都可以调用,优先选择调用不带const的那个(调用带有const的也需要转换);指针也类似的情况(如果实参是const对象,则调用带有const*类型形参的函数,否则如果实参不是const对象,将调用带有普通指针形参的函数)
f(int*)
f(const int*)// 可以重载
而下面的
f(int*)
f(int*const) //不能重载(值传递,副本传递) // 这里是const指针,只有指向const对象的指针作形参才能实现重载
再补充:仅当形参是引用或指针时,形参是否为const才有影响
int fuc2(int, int);
int fuc2(int, int b = 2); //重复声明
p229 建议:何时不重载函数名
21. 函数重载的前提是函数彼此间作用域相同,否则就是屏蔽关系
局部声明函数是个坏习惯,一般都有要声明在头文件中
C++中,名字查找发生在类型检查之前,所以,在局部作用域内找到名字对应的函数后,不再去外层找了,即使类型不是最合适的。
重载确定
找与实参最佳match
void f(int)
void f(double, double = 3.21)
调用f(3.2); 将选择下面那个,因为精确匹配优于需要类型转换的匹配。编译器将自动将默认实参提供给被忽略的实参,因此这个调用函数拥有的实参可能比显式给出的多
void f(int, int);
void f(double,double)
调用f(2, 3.24);有二义性
设计形参尽量避免实参类型转换
注意,较小的整型提升为int型。小的提升为int,优于形式上合适的char类型的匹配!
void f(int)
void f(short)
ff(‘a’) // match上面第一个
还有,类型提升优于其他标准转换!
例如:char到double优于到unsigned char的转换
void f(unsigned char) // 若去掉unsigned则选择第一个
void f(double)
调用f(‘a’) //二义
参数匹配与枚举类型
枚举类型的对象只能用同种枚举类型的另一对象或一个枚举成员进行初始化
整数对象即使有与枚举元素相同的值也不能用于调用期望获得枚举类型实参的函数,但是可以将枚举值传递给整型形参(129的时候提升为int,而不是unsigned char)
22. 函数指针:bool (*pf)(int); //指向函数的指针
而指针函数:bool *pf(int); // 返回值是指向bool指针
使用typedef简化函数指针
typedefbool (*pf)(int); //pf是一个种指向函数的指针的名字,它所指向的函数有一个int参数,返回值是bool类型
函数指针的初始化和赋值: (三种,0、函数名、&函数名)
第一种:pf fuc1 = 0;
//等于bool (*fuc1)(int)= 0; // 蓝色部分就是类型
第二种:使用函数名
注意:bool setAge(int); 其中setAge除了用于函数调用外,其他任何使用都将解释为指针: bool(*)(int);
pf fuc2 = setAge; // 使用函数名进行初始化
fuc1 = fuc2; //或赋值
上面直接使用函数名setAge等于在函数名上取地址操作
pffuc2 = setAge;
pffuc2 = &setAge; // 两个等价
而使用 pf fuc3(3); // 直接初始化是错的
指向不同类型函数的指针不存在转换
可以通过函数指针调用函数
fuc2 = setAge;
调用:fuc2(32); // 前提是这个函数指针已经初始化,若是0或未初始化不行
函数指针作形参
voidsamplefuction(const string&, bool(int));
//函数类型的形参,对应实参将被自动转换为相应的函数指针
voidsamplefuction(const string&, bool (*)(int));
//函数指针类型的形参,与上面声明等价
返回类型是指向函数的指针(返回类型可以是函数指针类型,但不可以是函数类型) 后补充:函数ff的返回类型是一个指向”外围”函数的指针
bool (*ff(int))(int*, int) // 蓝色部分是类型 这句话用在这里是在声明ff这个函数
ff(int)函数返回一个函数指针,它的类型是bool (*)(int*, int)
使用typedef使用该定义更加简明易懂
typedefbool (*PF)(int*, int)
PFff(int); // 声明ff这个函数
举例:
typedef int func(int*, int);
void f1(func); // ok,函数类型可以作形参,对应实参将自动转成函数指针
func f2(int); // error!! 返回类型不能是函数类型,不能自动转成函数指针
func *f3(int); // ok, 返回类型是函数指针
指向重载函数的指针
必须精确匹配
extern void ff(double);
extern void ff(int); // ok,重载
void (*pf)(double)= &ff; // ok, 精确匹配,对应第一个
int(*pf)(double) = &ff; // error, 返回值不匹配
第八章 IO
1. #include <iostream>
#include <sstring> // 从iostream继承
#include <fstream> // 从iostream继承
2. 国际字符支持
char有相应的wchar_t
wostream wistream wiostream <iostream>
wifstream wofstream wfstream <fstream>
wistringstream wostingstream wstringstream <sstream>
3. IO对象不能复制或赋值
不以放在vector或其他容器中
不能作为形参,必须是引用或指针(返回值也要是引用)
形参也是非const的,因为IO对象的读写会改变状态
4. 条件状态:
strm::iostate
strm::good 0 000
strm::badit 1 001 系统级
strm::failbit 2 010 可恢复
strm::eofbit 3 011
s.eof()
s.bad()
s.fail()
s.good()
s.clear() // 恢复所有
s.clear(f)
s.setstate(f) // 置为某条件发生
s.rdstate() // 返回当前条件 返回类型strm::iostate
5. testFile.cpp
作业
t8_3.cpp get.hpp
t8_6.cpp get.hpp
cout << “abc” << flush;
cout << “abc” << ends;
cout << “abc” << endl;
调bug时,根据最后一条输出确定崩溃的位置。多使用endl而不是’\n’,尽可能地让它在崩溃前将该刷新的输出出来
6. 交互式程序cin与cout绑定:任何cin读操作都会首先刷新cout关联的的缓冲区
tie函数,一个流调用tie将自己绑定在传过来的实参流上,则它的任何IO操作都会刷新实参所关联的缓冲区
cin.tie(&cout);
ostream *old_tie = cin.tie();
cin.tie(0); // 释放tie
cin.tie(&cerr);
cin.tie(0);
cin.tie(old_tie); // 重新与cout绑定
7. 文件流
ifstream infile(filename.c_str());
或
ifstream infile;
infile.open(filename.c_str());
if (!infile)
infile.close(); // 打开另一个前要关闭
infile.open(filename2.c_str());
如果要重用文件流读写多个文件,则在读另一个文件前要
infile.close();
infile.clear();
如果infile定义在while之中,那么每次都是干净的状态,不用clear都可以
8. vector中存放在着多个文件的名字
读出来
t8_8.cpp
if (!input) {
cerr<< "error: can not open file: " << *it << endl;
input.clear();
++it;
continue;
}
//在vs提示符下d:/dev_projects/c++primer>c++primer < data/8_8
9. t8_9.cpp读取文件内容,每个作为一个元素放到vector<string>中
ifstreaminfile(fileName.c_str());
if(!infile) {//打开文件失败
return1;
}
stringstr;
while(getline(infile, str)) { // 行作为元素
//while(infile >> str) { // 单词作为元素 8-10
svec.push_back(str);
}
10. 文件模式
in // ifstream和fstream , ifstream默认以in打开
out // ofstream fstream 会清空数据相当于也打开了trunc
ofstream默认以out打开
app // ofstream 追加
ate 打开后定位在尾
trunc // ofstream 清空数据
binary // 打开后以字节的形式重,不解释字符
ofstream outfile("data/testfile", ofstream::app);
fstream inout;
inout.open(“data/filename”, fstream::in |fstream::out);
ifstream& open_file(ifstream &infile,const string &filename) {
infile.close();
infile.clear();
infile.open(filename.c_str());
//if(!infile) // 不作判断
return in;
}
调用open_file()
ifstream infile;
if(! open_file(infile, fileName)) {
cout<< "error: can not open file:" << fileName << endl;
system("pause");
return-1;
}
get(infile);// 读取输出
get的实现
while (in >>ival, !in.eof()) {
if (in.bad()) {
throw runtime_error("IO streamcorrupted");
}
if (in.fail()) {
cerr << "bad data, tryagain!";
in.clear();
continue;
}
cout << ival << "";
}
11. 字符串流
while (getline(cin, line)) {
istringstreamistrm(line);
while(istrm >> word) { // 处理这行中的每个单词
.....
}
}
stringstream strm;
stringstream strm(s);
strm.str(); // 返回那个string对象
strm.str(s);
stringstream可以提供转换和格式化
可使自动格式化
teststringstream.cpp
int val1 = 21, val2 = 23;
ostringstream out ;
out << "val1:" << val1<< "\nval2:" << val2 << "\n";
cout << out.str() << endl;
cout <<"=======================\n" << endl;
istringstream in(out.str()); // 关联到这行字符val1:21\nval2:23
string dump;
int a,b;
in >> dump >> val1 >> dump>> val2; // 换成a和b就不行
//忽略所取数据周围的符号,用dump
cout << val1 << " "<< val2 << endl;
cin.clear(istream::failbit); 在我机器上不管用
要使用cin.clear()
12. 文件读入,每行作为一个元素放到vector<string>中,再istringstream对象初始化为每个元素,处理每个单词,输出; 这都是很简单的程序
istringstream istr;
string word;
for (vector<string>::const_iterator it= svec.begin();
it != svec.end(); ++it) {
istr.str(*it);
while (istr >> word) {
cout << word << “ ” ;
}
istr.clear(); // 定义在外面,所以将流设置为有效状态
}
第九章 顺序容器
1. vector.swap(): 两个人换家,家里东西不搬动
vector<string>sv1(10);
vector<string>sv2(32);
sv1.swap(sv2);// 不会使迭代器失效, 还指向原来的元素,常数时间内交换,没有移动元素
cout<< sv1.size() << endl; // 32
cout<< sv2.size() << endl; // 10
2. 9-28 不同类型容器间的赋值
char *sa[] = {"hello","good", "nihao", "leimng"};
list<char*> slst(sa, sa + 4);
vector<string>svec;
svec.assign(slst.begin(), slst.end());
3. size是已经有的,capacity是再分配之前的容量,通常大于size, reserve(n), 其中n>capacity,则改变的是capacity=n不会改变size
while (ivec.size() != ivec.capacity()) ivec.push_back(0); // 只要还有剩余的容量就不会重新分配
ivec.push_back(0); 此时再查看一下size和capacity,知道库的分配策略了。
4. 删除大写字母
for (string::iterator it = str.begin(); it !=str.end(); ++it) {
if(isupper(*it)) {
str.erase(it); // erase操作使it后移到被删元素的下一位置,而for中有++it,所以,下一步还原回来
--it; // 使迭代器指向被删除元素的前一位置
}
}
用while写可提高效率:
string::iterator it = str.begin();
while (it != str.end()) {
if(isupper(*it)) {
str.erase(it);
} else {
++it;
}
}
5. const vector<int> ivec(12);
vector<int>::iterator it = ivec.begin();// error,const类型迭代器不能赋给非const类型的it
改正:vector<int>::const_iteratorit = ivec.begin();
const vector<int>::iterator it =&ivec[0]; //error迭代器虽是指针,但不能这样赋值
6. 任何无效迭代器的使用都会导致运行时错误 ,无法检查出来
向vector添加元素,如果空间不够,则重新加载,原迭代器全部失效。
容器可以调整大小resize(n),有可能使迭代器失效。 对于vector,deque,有可能全部失效; 对所有的容器,如果压缩,则可能使被删除元素的失效
7. 元素类型size_type value_type, reference等同于value_type& 引用 ,写泛型程序非常用有
const_reference: value_type&
例子:vector<int>::size_type size = ivec.size(); / ivec.max_size();
ivec.resize(100,3); // 调整大小为100,新加元素为3
8. 向容器添加元素都是添加原值的副本
9. list容器前插法
while (cin >> word)
iter= ilst.insert(iter.begin(), word);
等同于:ilst.push_front(word);
10. 两个容器可以大小比较,其实是其元素在作比较,如果元素不支持,则容器也不支持
ivec1 < ivec2
要求完全匹配,即容器类型与元素类型都相同,且元素类型支持<操作
11. 访问元素
list<int>::referenceval = *ilst.begin(); // begin操作返回迭代器,其实是指针
list<int>::reference val2 =ilst.front(); // front和back操作返回元素的引用
ivec<int>::reference val3 = ivec[3]; 或 ivec.at(3); 随机访问类型的容器
12. 删除元素:
c.erase(p);
c.erase(b, e); // 返回下一元素的迭代器
pop_back(); 删除最后一个元素并返回void
pop_front 不返回被删除元素的值,想拿到这个值,就要在删之前调用front或back函数
清除clear
13. list是双向链表实现的 deque: 可以随机访问,中间插入元素代价高,但是在首尾删增元素代价小,且在首部增删元素不会使迭代器失效(当然删掉的就失效了)
如果读入的时候要在中间插,则可以先读入到list中,再拷到vector中,排一下序(书上说在拷之前排序)
14. string与vector不同的是,不支持栈方式操作容器,没有front, back, pop_back
15. vector<char> cvec初始化string str(cvec.begin(), cvec.end())
对于一个string对象,要一次输入一百个字符,则先str.reserve(101);改一下capacity
16. 以下是string的一些操作,构造,assign, erase, insert, append, replace, substr, find等等,使用的时候查
string构造另外方式:
string(c_array, n); //c_array可以是char *和char []的C风格字符串
string(s, pos2); //string类型s的下标pos2开始
string(s, pos2, len2);
替换str.assign(b, e); str.assign(n, t); //使用副本替换 返回string, 对于vector返回void
str.assign(s);// 以s的副本替换str
str.assign(c_array);
str.assign(s, pos2, len)
str.assign(cp, len)
str.erase(pos,len); // pos就是下标
str.insert(pos,cp); // 插入以NULL结尾的C风格串
str.insert(pos, s); // 插入s
str.insert(pos, s, pos2, len);
str.insert(pos, cp, len);
子串
str.substr(pos, n);
str.substr(pos);
str.substr(); // 返回str的副本
一系列的append和replace
查找:各种find, 返回类型string::size_type
find() // 找第一次出现
rfind() // 反向查找
find_first_of() // 任意字符的第一次出现 “0123456789” , 可以用来查找串中所有数字
find_last_of
find_first_not_of
find_last_not_of
没有找到if (find() != string::npos)
17. 容器适配器
#include <stack>
#include <queue>
stack与queue默认是建立在deque之上,这种情况可以改变,通过第二类型实参来指定:
stack<string, vector<string> > str_stk; // 前后string肯定一样啦
queue是队列,不能建立在vector之上,因为没有pop_front()操作
priority_queue默认建立在vector之上,也可以用deque,不能用list,它要求随机访问
适配器可以比较 > < =
优先队列也像栈一样有top操作,它是返回具有最高优先级的值,但没有back和front操作
优先队列在push操作的时候,自动插入到适当位置
对于容器这几章,建议使用的时候认真阅读primer书和参考文档
补充第九章:
1. 容器元素类型约束:赋值和复制运算。 不能是引用类型,不能IO类型
2. 支持随机访问的容器(vector, deque)的迭代器,支持it与数字的加减、it间的加减,it间的<>比较
3. 逆序输出ilist元素:
Ilist<int>::iterator it = ilist.end();
While (it != ilist.begin()) {
Cout << *(--it) << endl;
}
第十章 关联容器
1.pair在#include <utility>
pair<int, string> p1 = make_pair(2, “good”);
pair<int, string> p2(3, “hello”);
p1 < p2; 先比第一个,若相等,则比较第二个
p1.first 这两个成员都是公有成员
p1.second
利用typedef pair<int, string> auther;
auther p3(4, “nihao”);
由于是公胡成员,所以可以cin >> p1.first >> p1.second;
2. map类型
map的元素类型map<k, v>::value_type是一个pair<constk, v>类型, first元素是const map<k,v>::key_type, second是map<k,v>::mapped_type
map<string, int>::iterator map_it =word_count.begin();
map<string, int>::key_type str = map_it->first; // 键值const只能读不能改,跟set一样
word_count[“leiming”] = 1; // 直接插入
while(cin >> name) {
++word_count[name];// 如果没有,则直接++为1
}
pair<map<string,int>::iterator, bool> mp;
mp = word_count.insert(p1); //返回的pair是pair<map<string,int>::iterator,bool>类型
if (!mp.second) {
cout << “没有插入” << endl;
++mp.first->second;
}
在map中读而不插入元素
if (word_count.count(“fool”)){ // 返回0或1
occurs= word_count[“fool”];
}
或者
mpa<string, it>::iterator it =word_count.find(“fool”); // find返回指向按k索引的迭代器
if (it != word_count.end()) {
occurs= word_count[“fool”];
}
erase操作
erase(k) // 返回0,1
erase(p) // void
erase(b,e) // void
遍历输出word_count的时候,按字典顺序输出单词,很神奇!在使用迭代器遍历map容器时(别的时不行),迭代器指向的元素按key的升序排列