c++ primer 读书笔记

顺序容器:为程序提供控制元素存储和访问顺序的能力,这种顺序与元素加入到容器时的位置相对应,而与元素值无关。

另外还有根据关键字的值来存储元素的容器:有序、无序关联容器。

另外STL还有三种容器适配器,用来与容器类型适配。

顺序容器包括

vector 可变大小数组
deque 双端队列,支持随机访问,在front/back插入、删除速度很快
list 双向链表,只支持顺序访问,在任何位置插入元素很快
forward_list 单向链表,只支持单向顺序访问
array 固定大小数组,支持随机访问,不能添加、删除元素
string 与string相似的容器,专门用与保存字符,随机访问块,在尾部插入、删除速度快

除array外,其他的绒里都提供高效、灵活的内存管理(改变内存中的元素,数量,改变容器的大小)。

string/vector将元素存放在连续的内存空间中,可以随机访问,但是添加、删除元素的效率低。

list和forward_list 添加、删除元素都非常快,代价是不支持随机访问(为了访问某一个元素,需要遍历整个容器)

deque支持快速的数据访问,与string/vector相似,在中间为止添加/删除元素代价可能很高,但是在首尾位置添加/删除元素很快。

容器支持、

类型别名        解释
iterator    此容器类型的迭代器类型,就是下标|指针
const_iterator       可以读取元素,但是不能修改元素的迭代器类型
size_type    无符号类型,足够保存此种容器类型的最大可能容器的大小
difference_type   带符号整数类型,足够宝尊两个迭代器之间的距离

value_type  元素类型

reference  元素的左值类型,与value_type& 类型相同

const_reference  元素的const左值类型,即 const value_type &
=========================
构造函数  解释
C c;  默认构造函数,构造函数
C c1(c2);
C c(b,e);不支持array
C c{a,b,c...} 列表初始化c
=========================
赋值 与swap
c1 = c2;
c1 = {a,b,c...}
a.swap(b)
swap(a,b)
=======================
大小
c.size();不支持forward_list
c.max_size()
c.empty()
====================
添加删除元素/不适用于array
c.insert(args)
c.emplace(inits);使用inits构造c中的一个元素
c.erase(args);删除args制定的元素
c.clear()  ;删除c中所有的元素,返回void
=============================
关系运算符
==,!=  ;所有容器都支持相等(不等)运算符
<,<=,>,>=  ;关系运算符(无序关联容器不支持)
==================================
获取迭代器
c.begin(),c.end()
c.cbegin(),cend(),返回const_iterator
=====================
反向容器的额外成员(不支持forward_list)
reverse_iterator
const_reverse_iterator
c.rbegin(),c.rend();//返回指向c的尾元素和首元素之前位置的迭代器
c.crbegin(),c.crend()

9.2.3 begin和 end 成员

当不需要写访问时,应使用cbegin和cend成员

9.2.4容器定义和初始化

C c;

C c1(c2)

C c1 = c2;

C c{1,2,3...}

C c={1,2,3,...}

C c(begin,end);

---------只有顺序容器的构造函数才能接收大小参数

C seq(n)

C seq(n,t)

9.2.5

容器赋值运算

c1 = c2;

c = {1,2,c...}

swap(c1,c2);交换c1/c2中的元素,c1/c2必须具有相同的类型;swap通常比c2向c1拷贝元素快

c1.swap(c2);

assign操作不适用于关联容器和array

seq.assign(b,e)//

seq.assign(il)//将seq中的元素替换为初始化列表il中的元素

seq.assign(n,t)

####note: swap只是交换了两个容器的内部数据结构,除array外,swap不对元素进行拷贝,删除和插入操作,可以保证在O(1)时间内完成。

9.3

向vector/string/deque插入元素会使所有指向容器的迭代器iterator,引用reference,指针pointer失效,所以在跟新了容器的元素时,都应该更新迭代器,引用,指针。

在一个vector/string的尾部之外的任何位置,或是deque的首尾之外的任何位置天价了元素,都需要移动元素,而且向一个vector/string添加元素可能引起整个对象存储空间的重新分配。重新分配一个对象的存储空间需要分配新的内存,并将元素从旧的空间移动到新的空间中。

容器元素是拷贝

当文用一个对象来初始化容器时,或将一个对象插入倒容器中时,实际上放入到容器中的是对象值的一个拷贝,而不是对象本身。就像我们将一个对象传递给非引用参数一样,容器中的元素与提供值的对象之间没有任何关联了。随后对容器中元素任何改变都不会影响到原始对象,反之依然。

使用emplace操作,新标准引入了三个新成员emplace_front,emplace,emplace_back,这些操作不是构造而是拷贝元素,这些操作对应push_back,insert,push_back,允许我们将元素放置在容器头部,一个指定位置或容器头部

9.3.2访问元素

包括array在内的每个顺序容器都有一个front(),

除了forward_list外,每个顺序容器都有一个back(),分别返回首尾元素

在顺序容器中访问元素的操作
at和下标操作符只适用于string,vector,deque,array容器,back不能用于forward_list
c.back()
c.front()
c[n]
c.at[n]

访问成员函数返回都是引用,back,front,at,[]返回的都是引用,如果容器是一个const对象,则返回const引用。如果容器不是const,则返回值是普通引用,我们可以引用它改变元素的值:

    if(!c.empty()){
        c.front() = 42;
        auto &v = c.back();
        v = 1024;
        auto v2 = c.back();//如果我们使用auto变量来保存这些函数的值,并且希望使用此变量来改变元素的值,必须记得将变量定义为引用类型 auto &v = c.back();
        v = 0;
    }

9.3.3

顺序容器的删除操作
这些操作会改变容器的大小,不能用于array
forward_list 有特殊的erase,forward_list不支持pop_back;
vector/string 不支持pop_back
c.pop_back()
c.pop_front()
c.earse(p)
c.erase(b,e);
c.clear()

9.3.4 特殊的forward_list操作

forward_list当添加/删除一个元素时,会改变该元素之前、之后的元素指针;

forward_list没有定义insert,emplace,erase,而是定义了insert_after,emplace_after和erase_after的操作,所以在删除元素之前应该先知道这个元素的首前迭代器;

#################################

泛型算法:

算法如何工作

find的工作实在一个未排序的元素序列中寻找一个特定的元素,执行的步骤:

1,访问序列中的首元素

2,比较此元素与我们要查找的元素

3,如果此元素与我们要查找的值匹配,find返回标识此元素的值

4,否则,find前进到下一个元素,重复执行步骤2,3,

5,如果到达序列末尾,find停止

6,如果find到达序列末尾,他应该返回一个指出元素未找到的值。此值和步骤3返回的值必须具有相容的类型。

============

初始泛型算法:STL中提供了超过100个算法,有一致的结构,除了少数例外,每个算法都对一个范围内的元素进行操作,此范围就是“输入范围”,前闭后开【)。我们可以根据算法是否读取元素,改变元素,或者重排元素位置来进行区分。

##只读算法

find(vec.cbegin(),vec.end(),val)

accumulate(vec.cbegin(),vec.cend(),0);

对于只读而不改变元素的算法,通常最好使用cbegin()和cend();但是有时候根据需要,也需要使用begin(),end();

equal(vec.cbegin(),vec.cend(),vec2.cbegin()); equal假设:第二个序列至少与第一个序列一样长,

##写容器元素的算法

算法不会执行容器操作,因此他们本身不可能改变容器的大小。但是会修改容器的内容。

fill(vec.begin(),vec.end(),0);

算法不要求比较的容器都相同,但是他们的容器类型都必须相同。equal(vec.begin,vec.end(),list1.begin());ok

插入迭代器back_inserter(),定义在头文件iterator中的一个函数;接受一个指向容器的引用,返回一个与该容器绑定的插入迭代器。

vector<int> vec;
auto it = back_inserter(vec);
*it = 32;

拷贝算法copy,另一个向目的迭代器指向的输出序列中的元素 写入数据的算法。 接受三个迭代器,前两个是输入范围,第三个表示目的序列的起始位置。

int a[] = {1,2,3,4,5,6,7};
int a2[sizeof(a)/sizeof(*a)];a2与a大小一样
auto ret = copy(begin(a),end(a),a2);//ret = a2.end()

note:copy(begin(a1),end(a1),a2)  a2的大小要与a1一样长

replace_copy(ilsit.begin(),ilist.end(),0,2);将ilist中所有的0改变为2

##重排容器元素的算法

sort,会重排输入序列中的元素,使之有序,利用元素类型<(小于)运算符来实现的

unique, 消除重复单词,首先将vector排序,使得重复的单词相邻出现。返回的位置是  一个指向  不重复值 范围末尾的迭代器。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

void dlimDups(vector<string> &words){
    sort(words.begin(),words.end());

    for(auto i: words){
        cout<<i<<" ";
    }cout<<endl;

    auto end_unique = unique(words.begin(),words.end());
    for(auto i: words){
        cout<<i<<" ";
    }cout<<endl;

    words.erase(end_unique,words.end());

    for(auto i: words){
        cout<<i<<" ";
    }cout<<endl;
}

int main()
{
    vector<string> vstr = {"the","quick","red","fox","jumps","over","the","slow","red","turtule"};
    cout<<"vstr.length = "<<vstr.size()<<endl;

    for(auto i:vstr){
        cout<<i<<" ";
    }cout<<endl;

    dlimDups(vstr);

    return 0;
}

##定制操作算法

sTL允许我们定义自己的操作来代替默认运算符,sort算法默认的比较操作符是<,我们可以定义自己的操作顺序>,>=等。

我们可以向算法传递函数,或者lambda表达式;

如果我们希望单词排序程序输出,先按长度排序,大小相同的再按字典顺序来排。

定义sort(vec.begin,vec.end(),predicate),

predicate谓词,STL算法使用的谓词分为两类:一元谓词(接受一个参数),二元谓词(有两个参数);由于接受谓词参数的算法对输入序列中的元素调用谓词,那么元素类型必须能转换为谓词的参数类型。

bool isShorter(const string &s1, const string &s2){
   return s1.size() < s2.size();  
}

这个二元谓词函数,在输入序列中所有可能的元素值上面定义了一个一致的序。

sort(words.begin(),words.end(),isShorter);//按照长度由短至长排序。

在我们words是按照大小排序的同时,如果还需要长度的元素按字典序排序。这是我们需要使用稳定排序算法: stable_sort(words.begin(),words.end(),isShorter);

lambada表达式以后再说。

 ===========================================================

第11章定义关联型容器

map/set容器

map中的元素是key-value对;set中每个元素只包含一个关键字;

set支持高效关键字查找操作---检查一个给定关键字是否在set中,可以定义忽略掉的单词等。

标准库中提供了8个关联容器,体现在3个不同的维度上:

1,或者是一个set,或者是一个map
2,或者要求不重复关键字,或者允许重复关键字
3,按顺序保存元素,或无序保存元素;

具体的类型有:

map/set/mutlimap/multiset/

unordered_map/unordered_set/

unordered_multimap/unordered_multiset

11.2 关联容器概述

定义关联容器:

map<string,size_t> word_count;

set<string> excluds = {"1","b","c"};

map<string,string> author = {{"b","b_value"},{"c","c_value"}};

初始化multimap/multiset,一个map/set中的关键字必须是唯一的,即对于一个给定的关键字,只能有一个元素的关键字对应它。

vector<int> ivec;

for(vector<int>::size_type i = 0;i != 10;i++){

  ivec.push_back(i);ivec.push_back(i);

 }

set<int> iset(ivec.cbegin(),ivec.cend());

11.2.2 关键字类型的要求

默认情况下,STL使用关键字类型的小于符号来比较两个关键字;在set类型中,关键字类型就是元素类型;在map类型中,关键字类型是元素的第一部分的类型。

可以向一个算法提供我们自己定义的比较操作,也可以定义自己定义的操作来代替关键字小于运算服。所提供的操作必须在关键字类型上定义一个严格弱序,这个严格弱序不一定是“小于等于”。

使用关键字类型的比较函数,为了指定使用自定义的操作,必须在定义关联容器类型时提供此操作的类型,用尖括号支出要定义的哪种类型的容器,自定义的操作类型必须在尖括号紧跟着元素类型给出。

bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs){

  return lhs.isbn() < rhs.isbn();

}

multiset<Sales_data, decltype(compareIsbn)*>  bookstore(compare)  --->C++ primer chinese Page379

11.2.3 pair type

#include <utility>

pair<type_1,type_2> mypair;

pair<T1,T2> p;
pair<T1,T2> p(v1,v2);
pair<T1,T2> p = {v1,v2};
make_pair(v1,v2);
p.first
p.second
p1 relop p2// <, > ,<=, >=  //c++ primer 5th Page 380
p1 == p2
p1 != p2

11.3

key_type 容器的关键字类型

mapped_type 每个关键字关联的类型,只用于map,就是值类型;例如,map<string,int>::mapped_type v5;//v5 is int

value_type 对于set来说,它与keytype相同;对于map来说, is pair<const key_type,mapped_type>

定义关联型迭代器,当解引用一个关联容器迭代器时,我们会得到一个类型为容器的value_type,对于map来说,value—type是一个pair类型,其first成员是一个const关键字,但是second不是const的

另外set的迭代器是const,就是不能利用解引用符号来修改set中的元素值。例如×set—it = 42,是不允许的,因为set—it是一个只读类型

关联容器和算法,通常我们不对关联容器使用泛型算法。const意味着不能将关联容器传递给修改或重排容器元素的算法,因为这类算法需要向元素写入值,set类型中的元素是const,map中的元素是pair,但是第一个元素是const;

so,关联容器只能用于读取元素的算法,但是很多这类算法,需要搜索序列,由于关联容器中的元素不能通过他们的关键字进行快速查找,因此对其使用泛型算法没有什么意思。但是关联容器总是有一个find成员,通过一个给定的关键字来直接获取元素。

11.3.2 添加元素

利用insert函数,或者emplace函数,

对于set来说,插入重复数据,没有什么影响;

对于map来说,需要插入的pair类型,因此需要利用原始数据创建pair;insert的返回值是一个pair,pair的first成员是一个迭代器,指向具有给定关键字的元素,second成语根据是否是新插入的元素返回true(是)/false(原来就有的)

初级展开语句

ret

ret.first

ret.first->  

ret.first->second

++ret.first->second     =  ++((ret.first)->second);

11.3.3 delete elements

c.erase(k);//kye = k, return size_type,  is a quantity of deleted elements

c.erase(p); // p is a iterator, is the next element of deleted elemtent

c.erase(b,e)

11.3.4 map index control

there is not [] in set.

11.3.5 access elements

find();

原文地址:https://www.cnblogs.com/li-daphne/p/5450885.html