cplusplusprimer_note8

String类和标准模板库

NBTS null-terminated string 以空字符结束的字符串,即传统c风格字符串

string构造函数:

构造函数 描述
string(const char * s) 将string对象初始化为s指向的NBTS
string(size_type n, char c) 创建一个包含n个元素的string对象,每个元素被初始化为c
string(const sring & str) 复制构造函数
string() 默认构造函数
string(const char * s, size_type n) 将string对象初始化为s指向的NBTS的前n个字符,即使超过结尾
template\<class Iter>
string(Iter begin,Iter end)
string对象初始化为区间[begin,end)内的字符Iter可以是迭代器也可以是指针
string(const string & str, string size_type pos=0, size_type n = npos) 复制构造函数加强版
string(string && str) noexcept 将一个string对象初始化为string对象str,并可能修改str(移动构造函数)
string(initializer_list\<char> it) string对象初始化为初始化列表li中的字符{‘S','t','r'}

+=, =运算符被重载多次使得能接受string对象,char字符,c风格字符串对象等

//c风格字符串接受输入:
char info[100];
cin >> info;//read a word
cin.getline(info,100); //read a line, discard \n
cin.get(info,100); //read a line, leave \n

//string
string stuff;
cin >> stuff; //read a word;
getline(cin, stuff);//read a line, discard \n

//两种getline都有可选的指定边界的参数
cin.getline(info,100, ':');
getline(cin, stuff, ":"); 

//string版本的getline能自动调整输入大小,使之刚好能储存
//读c风格的字符串函数是istream类的方法,而string版本是独立的函数

char fname[10];
string lname;

cin >> fname;
cin >> lanme;

cin.operator>>(fname);
operator>>(cin, lname);//长度限制为string::npos一般为unsigned int值

string版本的getline函数停止条件:

  • 到达文件未,输入流eofbit位被设置,fail()和eof方法返回true
  • 分界符如\n,删除它后停止
  • 读取字符数达到 最大值,failbit被设置,fail方法返回true

输入流对象的寄存器:

  • failbit输入错误
  • eofbit文件结尾
  • badbit故障
  • goodbit一切顺利

string版本的operator>>()函数行为类似,但是不删除空白符

比较字符串:每个关系运算符都以三种方式重载,以支持string与c风格字符串、string与string、c风格字符串与string比较

size()为提供STL兼容性后加的,length()早期版本

重载的find方法:

方法原型 描述
size_type find(const string & str, size_type pos=0)const 从pos开始查找str,找到返回第一次出现的索引,否则返回npos
size_type find(const char * str, size_type pos=0)const
size_type find(const char * str, size_type pos=0, size_type n)const 查找str的前n个字符组成的字符串
size_type find(char ch, size_type pos=0)const 查找字符ch

rfind()、find_first_of()、find_last_of()、find_first_not_of()、find_last_not_of()

rfind()查找字符串最后一次出现的位置

find_first_of()查找参数值任何一个字符首次出现的位置

capacity():返回当前string使用的内存块大小

reserve(int n):请求内存块的最小长度

c_str():返回c风格字符串

fout.open(filename.c_str());

智能指针模板类

行为类似于指针的类对象

  • auto_ptr c++11已经废弃
  • unique_ptr
  • shared_ptr

包含头文件memory

auto_ptr<double> pd(new double);

不能将普通指针直接赋值给智能指针对象,隐式转换不允许,但是普通指针可以做智能指针构造函数的参数

auto_ptr:赋值操作转让了所有权

引用计数,赋值时引用加一,指针过期时指针减一

unique_ptr:程序试图将一个unique_ptr赋值给另一个时,如果源unique_ptr是一个临时右值,编译器允许这样做,如果unique_ptr将存在一段时间,则禁止,会在编译阶段报错。

unique_ptr<string>pu1(new string("aa"));
unique_ptr<string> pu2;
pu2 = pu1; //compile time error
pu2 = move(pu1);
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string("xx"));//allowed

unique_ptr优于auto_ptr:

  • 更安全

  • auto_ptr没有new [] 和delete []版本,因此允许用于数组的变体

    std::unique_ptr pda(new double(5)); //will use delete []
void show(unique_ptr<int> & pi) {//这里必须按引用传递而不能按值传递,不然就会导致一个非零时的unique_ptr初始化值pi

}

可将右值的unique_ptr作为构造参数构造shared_ptr,shared_ptr将接管原来归unique_ptr的对象

STL标准模板库

所有stl容器提供了一些基本方法:size(), swap(), begin(), end()

迭代器:指针或者可以对其执行类似指针操作的对象

每个容器类都定义了迭代器:

vector<double>::iterator pd;

vector<double> scores;
pd = scores.begin();
*pd = 22.3;
++pd;

vector独有的方法:push_back,erase(iter1, iter2)

scores.erase(score.begin(), socre.begin()+1);

insert(iter1, iter2start, iter2end)

find()非成员函数

swap既有也有成员函数,但是成员函数效率高,非成员函数能支持交换两个不同类型容器的内容

非成员函数for_each(iter1, iter2, fptr)

vector<Review>::iterator pr;
for (pr = books.begin(); pr != books.end(); pr++) {
    ShowReview(*pr);
}

//替换为
for_each(books.begin(),books.end,ShowReview) // 被指向的函数不能修改容器的值

非成员函数random_shuffle(iter1, iter2)随机排列该区间中间的元素 要求容器类允许随机访问

非成员函数sort 要求随机访问sort(iter1, iter2)

范围for循环:

for(auto x : books) ShowReview(x);
//不同于for_each,可以修改容器的值,方法是传入引用
for(auto & x : books) 

operator++作为前缀版本

operator++(int)作为后缀版本

最好避免直接使用迭代器而是使用stl函数比如for_each()或者基于范围的for循环

5种迭代器,

  • 输入迭代器 不会修改容器中的值,只读 单次通行算法
  • 输出迭代器 只修改容器中的值,不读取
  • 正向迭代器 总是按照相同的顺序遍历一遍值 多次通行算法
  • 双向迭代器
  • 随机访问迭代器 增加了+n操作
迭代器功能 输入 输出 正向 双向 随机

stl算法也可用于常规数组

copy()数据赋值,容器到容器,数组等

int casts[10] = {1,2,3,45,6,7,8,9,10};
vector<int> dice[10];
copycasts, casts+10, dice.begin());
//前两个参数最好是输入迭代器,后一个参数最好是输出迭代器

#incluce<iterator>

//int是被发送给输出流的类型,char是输出流使用的字符类型
//cout是要使用的输出流,“ ”是分隔符
ostream_iterator<int, char> out_iter(cout, " ");
*out_iter++ = 15;//类似于cout << 15 << " ";
//对于常规指针,表示把15赋值给指针指向的位置,然后指针加一
//对于输出流指针,表示把15和空格字符串发送给cout管理的输出流中并为下一个输出操作做好准备

copy(dice.begin(), dice.end(), out_iter);//容器内容复制到输出流中,即显示容器内容
copy(dice.begin(), dice.end(), ostream_iterator<int, char> out_iter(cout, " "));//匿名

//使用cin参数表示使用cin管理的输入流,省略cin表示输入失败
//从输入读取直到失败为止
copy(istream_iterator<int, char>(cin), istream_iterator<int,char>(), dice.begin());

copy(dice.rbegin(), dice.rend(), out_iter);//display in reverse order
//rbegin和end返回相同的值,但是类型不同,(reverse_iterator, iterator)
//对反向迭代器递增将会递减
//反向迭代器先递减,再解除引用

//使用stl函数处理内部问题(copy。。。rbegin)优于显示声明迭代器(++操作)

插入迭代器自动调整了容器长度

  • back_insert_iterator 尾部插入迭代器 只允许在结尾做时间固定的插入的容器类型 vector queue
  • front_insert_iterator 头部插入迭代器 只允许在头部做时间固定的插入的容器类型 queue vector不行
  • insert_iterator 没有限制

将容器类型作为模板参数,将实际容器标识符作为构造函数参数

back_insert_iterator<vector<int>> back_iter(dice); 
//back_i_i 将假设传递给他的容器类型有一个push_back方法,copy不能调整容器大小,但是back_i_i就可以了

insert_iterator<vector<int>> insert_iter(dice, dice.begin());

容器类型dequeue,list,queue, priority_queue,stack, vector, map, multimap, set, multiset,bitset

c++11:forward_list, unordered_map, unordered_multimap, unordered_set,unordered_multiset

容器概念:容器的基本特征

表达式 返回类型 说明 复杂度

c++11新增:

复制构造和复制赋值与移动构造和移动赋值之前的区别:赋值操作保留源对象,移动操作可修改源对象,还可能转让所有权而不做任何复制。如果源对象是临时的,则移动操作效率更好

序列概念 比如deque,list,queue,priority_queue,stack,vector

必须都是正向迭代器

序列的要求

序列的可选要求

deque:支持随机访问,支持头部插入比vector稍微慢一些

list的一些成员函数

对list不能使用非成员函数sort,只能使用成员函数版本的sort

forward_list:单向链表,正向迭代器不用双向迭代器不可反转

queue是适配器类,底层是deque, 限制多于deque

queue的操作:

pop这里只负责删除,若要查看要使用front

priority_queue是适配器类,默认底层是vector,最大的元素放在队首

stack:适配器类,底层为vector

stack操作:

array:c++11 头文件array 不是stl容器,长度固定没有调整大小的操作,但是有成员函数,可以使用stl的标准算法如copy和for_each

关联容器

可以插入,不能指定位置插入

  • set 值和键的类型相同,不允许多个值相同
  • multiset值和键的类型相同,允许多个值相同
  • map 值与键类型不同,键是唯一的,每个键对应于一个值
  • multimap 值与键类型不同,键是唯一的,每个键对应于多个值
set_union(a.begin(), a.end(), b.begin(), b.end(), ostream_iterator<string,char> out(cout, " "));
//第五个参数是输出迭代器,要将结果集合输出到什么位置
//不能写c.begin()因为这是一个常量迭代器关联集合将键看作常量,不能用作输出迭代器。而且set_u类似于copy将覆盖容器已有的数据,并要求容器有足够空间,因此可以用insert_iterator
set_union(a.begin(), a.end(), b.begin(), b.end(), insert_iterator<set<string>> ());

multimap和set一样是经过排序的可翻转的关联容器

multimap<int, string> codes;
pair<const int, string> item(123, "Los");
cout << item.first << item.second;
codes.insert(item);

count low_bound upper_bound equal_range:

auto range = codes.equal_range(718);
for (auto it = range.first; it != range.second; it++)
    cout << (*it).second;

无序关联容器

函数对象

以函数方式与()结合使用的任意对象,包括函数名,指向函数的指针,重载了()的类对象

  • 生成器generator不用参数就可以调用的函数符
  • 一元函数 unary function是用一个参数可以调用的函数符
  • 二元函数 binary funciton是用两个参数可以调用的函数符
  • 返回bool的一元函数是谓词 predicate list.remove_if(f)
  • 返回bool的二元函数是二元谓词 binary predicate

类函数符能用来适配不同的接口

预定义的函数符

transform:

transform(fromiterstart, fromiterend, toiterator, f);
transform(fromiterstart, fromiterend, fromiterstart2,toiterator, f);
#include <functional>
plus<double> add;
double y = add(1.1,2.2);

transform(a.begin(), a.end(), b.begin(), out, plus<double>());

运算符和相应的函数符

  • binder1st:将二元自适应函数转化为一元函数,

    //f2为二元函数
    binder1st(f2, val) f1;
    f1(x);
    f2(val, x);
    
    bind1st(multiplies(), 2.5);//直接返回一个这样子的对象

算法

  • 非修改式序列操作 find for_each
  • 修改式序列操作 transform random_shuffle copy
  • 排序和相关操作 sort 集合操作等
  • 通用数字运算 区间内容累计,两个容器的内部乘积,相邻对象差等,vector最可能

前三组在头文件algorithm定义第四个在numeric

区间参数至少是输入迭代器类型。指示结果储存位置的迭代器必须是输出迭代器

sort是就地算法 copy是复制算法, transform都可以

有的算法有两个版本,复制版本的以copy结尾,同时接受一个额外的输出迭代器参数

对于复制算法,同意约定返回一个迭代器,指向复制去的最后一个值的后面OutputIterator

_if结尾 谓词判断 replace_if如果将函数用于旧值时,返回true则替换

string类不是STL的组成成员,但是可以使用stl接口

有时有stl方法和stl函数可以选择,通常方法更好,因为更适合于特定的容器,而且作为成员函数可以使用模板类的内存管理工具从而在需要时调整容器长度

list<int> la;
la.remove(4); //remove all 4s from the list
list<int>::iterator last = remove(la.begin(), la.end(), 4);
//不能调整链表长度,将没被删除的元素放在链表的开始位置,并返回一个指向新的超尾值的迭代器,接下来可以用该迭代器来修改容器长度,比如接着:
la.erase(last, la.end());
set<string> wordset;
transform(words.begin(), words.end(), insert_iterator<set<string>>(wordset, wordset.begin()),ToLower);

string & ToLower(string & st)
{
    transform(st.begin(), st.end(), st.begin(), tolower);
    return st;
}
count(iter1, iter2,elem);//返回elem出现的次数

三个数组模板:

  • vector 面向容器
  • valarray 面向数值计算 不是stl 适用于数学计算,但是通用性更低
  • array 代替内置数组 能使用一些stl方法

不同的小组开发的用于不同目的。

模板 initializer_list

容器类包含将initializer_list作为参数的构造函数

vector<double> payments{xxx};
vector<double> payments({xxx});

vector<int> vi{10};
//到底是:
vector<int> vi(10);//10 未初始化元素
vector<int>vi({10});//一个元素初始化成10

//优先B

除非类要用于处理不同长度的列表,否则让它提供接受initializer_list作为参数的构造函数没有意义

在代码中使用initializer_list要包含头文件initializer_list

double sum(std::initializer_list<double> il)
{
    for (auto p = il.begin(); p != il.end(); p++)
        xxx;
}

stl是按值传递initializer_list对象的

()

发表评论