c++11学习笔记(5)

(1)deque双端队列是一个更为复杂的数据结构,与string和vector类似,deque支持快速随机访问,并且也是在deque的中间位置添加或删除元素的代价很高。但是,在deque的两端添加或删除元素都是很快的,和list或forward_list的速度相当。
(2)forward_list和array是新c++标准增加的类型。和内置数组相比,array是一种更安全更容易使用的数组类型,其大小和内置类型一样是固定的,不支持添加或删除元素以及改变容器大小的操作。forward_list的设计目标是达到和最好的手写的单向链表数据结构相当的性能,因此,没有size的操作,因为保存或计算其大小就会比手写链表多出额外的开销。
(3)对其他容器而言,size保证是一个快速的常量时间的操作。
(4)除非有很好的理由选择其他容器,否则应该使用vector。
(5)如果程序有很多小的元素,且空间的额外开销很重要,则不要使用list或forward_list。
(6)如果程序要求随机访问元素,应使用vector或deque。
(7)如果程序要求在容器的中间插入或删除元素,应使用list或forward_list。
(8)如果程序需要在头尾位置插入或删除元素,但不会在中间位置插入或删除,则使用deque。
(9)如果程序只有在读取输入的时候才需要在容器中间位置插入元素,随后需要随机访问元素,则首先,要确定是否真的需要在容器中间位置添加元素。当处理输入数据的时候,通常很容易向vector追加数据,然后再调用标准库的sort函数来重排容器中的元素,从而避免在中间位置添加元素。
(10)如果必须在中间位置插入元素,考虑在输入阶段使用list,一旦输入完成,将list中的内容拷贝到一个vector中。
(11)如果不确定使用哪种容器,那么可以在程序中只使用vector和list公共的操作,使用迭代器,不使用下标操作,避免随机访问。这样,在必要的时候选择vector和list都很方便。
(12)和指针类似,也能通过解引用迭代器来获取它的元素,执行解引用的迭代器必须合法并确实指示着某个元素。

string S("some strung")
//把第一个元素变成大写
if(s.begin()!=s.end())
{
  auto it = s. begin();
  *it = toupper(*it);
}

(13)迭代器使用递增运算符从第一个元素移动到下一个元素。因为end返回的迭代器并不实际指示着某个元素,所以不能对其进行递增或解引用的操作。现在利用迭代器及其递增运算符可以实现把所有的元素都变成大写。

for(auto it != s.begin; it != s.end() && isspace(*it); ++it)
    *it = toupper(*it);

(14)泛型编程:c++程序员习惯性使用!=原因在于他们更愿意使用迭代器而非下标的原因一样,因为这种编程风格在标准库提供的所有容器上都有效。
(15)就像不知道string和vector的size_type成员到底是什么类型一样,一般我们也无须知道迭代器的精确类型。实际上,那些拥有迭代器的标准库类型使用iterator和const_iterator来表示迭代器的类型。

vector<int>::iterator it;
string::iterator it2;
vector<int>::const_iterator it3;
string::const_iterator it4;

(16)begin和end返回的具体类型由对象是否是常量决定,如果对象是常量,begin和end返回const_iterator,如果对象不是常量,返回iterator。

vector<int> v;
const vector<int> cv;
auto it1=v. begin();
auto it2=cv. begin();

有时候这种默认的行为并非我们索要,如果对象只需读操作而无须写操作的话最好使用常量类型,为了便于专门得到const_iterator类型的返回一个,c++新标准引入了两个新函数,分别是cbegin和cend。

auto it3=v. cbegin()

(17)解引用迭代器可以获得迭代器所指的对象,如果该对象的类型恰好是类,就有可能希望进一步访问它的成员。例如,对于一个由字符串组成的vector对象来说,要想检查其元素是否为空,另it是该vector对象的迭代器,只需要检查it所指字符串是否为空就可以。

(*it). empty()

(18)为了简化上述表达式,c++语言定义了箭头运算符,把解引用和成员访问两个操作结合在一起,也就是说,it->mem和(*it). mem表达的意思相同。

for(auto it=text.cbegin(); it!=text.end() && !it->empty(); ++it)
    cout << *it <<endl;

(19)string和vector的迭代器提供了更多额外的运算符,一方面可使得迭代器的每次移动跨过多个元素,另外也支持迭代器进行关系运算,所有的这些运算被称作迭代器运算。
(20)下面的代码得到一个迭代器,它指向某vector对象中间位置的元素。

auto mid = vi. begin() + vi. size() / 2;

(21)对于string和vector的迭代器来说,除了判断是否相等,还能使用关系运算符(小于大于)等进行比较,参与比较的两个迭代器必须合法而且指向同一个容器元素,或者尾元素的下一个位置。

if (it < mid)
    处理vi前半部分的元素

(22)只要两个迭代器指向的是同一个容器中的元素或者尾元素的下一个位置,就能将其进行相减,所得结果是两个迭代器的距离。所谓距离就是指右侧的迭代器向前移动多少位置就能追上左侧的迭代器,其类型是名为difference_type的带符号整数,string和vector都定义了difference_type,因为这个距离可正可负,所以difference_type是带符号类型的。
(23)使用迭代器运算的一个经典算法是二分搜索,二分搜索从有序序列中寻找某个给定的值。二分搜索从序列的中间位置开始搜索,如果中间位置的元素正好是要找的元素,搜索完成,如果不是,假如该元素小于要找的元素,则在序列的后半部分继续搜索,假如该元素大于要找的元素,则在序列的前半部分基于搜索。在缩小的范围中计算一个新的中间元素并重复之前的过程,直至最终找到目标或者没有元素可供继续搜索。(24)这个程序使用迭代器完成二分搜索

复制代码
auto beg = text. begin(), end = text. end();
auto mid = text. begin() + (end - beg) / 2;
//当还有元素尚未检查并且我们还没有找到sought的时候执行循环
while(mid != end && *mid != sought)
{
    if(sought < *mid)
        end = mid;//要找的元素在前半部分
    else
        beg = mid + 1;
    mid = beg + (end - beg) / 2;
    //设置新的中间点
}                   
复制代码

(25)迭代器范围的概念是标准库的基础。一个迭代器范围由一对迭代器表示。

posted @   花与不易  阅读(107)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示