c++primer之顺序容器

本文详细介绍了C++标准库中的顺序容器,如vector、deque、list、forward_list和array的特点与使用场景。重点讨论了它们在元素插入、删除和访问速度上的差异,并给出了选择容器的原则。例如,vector适合随机访问,list适合中间插入删除,deque在头尾操作高效。同时,文章还提到了容器适配器如stack、queue和priority_queue的应用。
摘要由CSDN通过智能技术生成

一、顺序容器提供了控制元素存储和访问顺序的能力

二、所有顺序容器都提供了快速顺序访问元素的能力,但向容器添加或删除元素以及非顺序访问容器中的元素都有不同的性能代价

三、顺序容器

1、vector可变大小数组, 支持快速随机访问,在尾部以外的位置插入或删除元素较慢

2、deque双端队列, 支持快速随机访问,在头尾位置插入或删除元素很快

3、双向链表, 只支持双向顺序访问,在任何位置插入或删除都很快

4、forward_list单向链表, 只支持单向顺序访问,在任何位置进行插入或删除都很快

5、array固定大小数组,支持快速随机访问,不能添加或删除元素

6、string与vector相似,但只用于保存字符

说明:

string和vector将元素保存在连续的内存空间中,由于元素是连续存储的,由元素下标来计算其地址很快,但在容器的中间位置添加或删除元素就很耗时,在一次插入或删除操作后,需要移动插入或删除位置之后的所有元素来保持连续存储,而且,添加一个元素有时候可能还需要分配额外的存储空间,在这种情况下,每个元素都必须移动到新的存储空间中

list和forward_list的设计目的是令容器任何位置的添加或删除都能很快,作为代价,这2个容器都不支持元素的随机访问,为了访问一个元素,只能遍历整个容器,forward_list没有size操作

deque支持快速随机访问,但在中间位置插入或删除元素很慢,在首尾插入或删除元素都很快

四、选择容器的原则

1、除非有很好的理由选择其他容器,否则应使用vector

2、如果程序要求随机访问元素,应使用vector或deque

3、如果程序需要在容器的中间插入或删除元素,应使用list或forward_list

4、如果程序需要在头尾位置插入或删除元素,但不会在中间位置进行插入或删除操作,应使用deque

5、如果程序只有在读取输入时才需要在容器中间插入元素,随后需要随机访问元素则:

(1)、确定是否真的要在容器中间位置添加元素,当处理输入数据时,通常可以很容易的向vector追加数据,然后再调用标准库的sort函数来重新排序容器中的元素

(2)、如果必须再中间位置插入元素,考虑在输入阶段使用list,输入完成后将list中的内容拷贝到一个vector中

五、容器类型成员

1、iterator此容器类型的迭代器类型

2、const_iterator可以读取元素,但不能修改元素的迭代器类型

3、size_type无符号整数类型,足够保存此种容器类型最大可能容器的大小

4、difference_type带符号整数类型,足够保存2个迭代器之间的距离

5、value_type元素类型

6、reference元素的左值类型,与value_type&含义相同

7、const_reference元素的const左值类型,与const value_type&含义相同

六、 begin()、end()、cbegin()、cend()、rbegin()、rend()、crbegin()、crend()

七、将一个容器创建为另一个容器的拷贝的方法有2种

1、直接拷贝整个容器, 容器类型及元素类型必须匹配

2、拷贝由一个迭代器对指定的元素范围, 容器类型及元素类型可以不匹配,但元素类型需要能转换

八、不能对内置数组类型进行拷贝或对象赋值操作,但array并无此限制

九、由于两边颜色对象的大小可能不同,所以array类型不支持assign,也不支持花括号包围的值列表赋值。

十、assign允许从一个不同但相容的类型赋值,或者从容器的一个子序列赋值,assign操作用参数指定的元素替换左边容器中的所有元素

十一、成员函数size返回容器中元素的数目; empty当size为0时返回true; max_size返回一个大于或等于该类型容器所能容纳的最大元素数的值; forward_list支持max_size和empty,但不支持size

十二、容器操作

1、除了array和forward_list,每个顺序容器都支持push_back

2、list、forward_list和deque支持push_front

3、vector、list、deque、string支持insert, forward_list提供了专有的insert

4、emplace_front对应push_front; emplace对应insert; emplace_back对应push_back; 这3个成员操作构造而不是拷贝元素,当调用push或insert成员函数时,对象被拷贝到容器中,当调用emplace时,则是将参数传递给元素类型的构造函数

5、string、vector、deque和array支持下标运算符,也提供了at成员函数,与下标运算符不同的是,下标越界会直接报错,而at会抛出out_of_range异常

6、erase可以删除由一个迭代器指定的单个元素,也可以删除由一对迭代器指定的范围内的所有元素,他们都返回指向删除元素之后位置的迭代器

7、forward_list未提供insert、emplace和erase,但提供了insert_after、emplace_after、erase_after和before_begin/cbefore_begin, before_begin返回首前迭代器,这个迭代器允许在链表首元素之前并不存在的元素“之后”添加或删除元素, 当在forward_list中添加或删除元素时,需要注意2个迭代器:一个指向要处理的元素,一个指向其前驱

8、resize如果当前大小大于所要求的大小,容器后部的元素会被删除,如果当前大小小于新大小,会将新元素添加到容器后部

十三、容器操作可能使迭代器失效

向容器中添加元素:

1、如果容器是vector或string,且存储空间被重新分配,则指向容器的迭代器、指针和引用都会失效,如果存储空间未重新分配,指向插入位置之前的元素的迭代器、指针、引用仍然有效,但指向插入位置之后的任何位置都会导致迭代器、指针、引用失效

2、对于deque,插入到除了首尾位置之外的任何位置都会使迭代器、指针、引用失效,如果在首尾位置添加元素,迭代器会失效,但指向存在的元素的指针和引用不会失效

3、对于list和forward_list,指向容器的迭代器、指针、引用仍有效

从容器中删除元素

1、对于list和forward_list,指向容器其他位置的迭代器、指针、引用仍有效

2、对于deque,如果在首尾之外的任何位置删除元素,那么指向被删除元素外其他元素的迭代器、指针、引用会失效,如果删除尾元素,则尾部后迭代器会失效

3、对于vector和string,指向被删除元素之前的迭代器、指针、引用仍有效

4、当删除元素时,尾后迭代器总是会失效

十四、为了快速随机访问,vector将元素连续存储,那么添加元素时,如果没有空间容纳新元素,容器不可能简单的将它添加到内存中的其他位置,必须重新分配新的内存空间来保存已有的元素和新元素以保持连续,将已有的元素从旧位置移动到新空间中,然后添加元素,释放旧存储空间,所以如果每新增一个元素,就指向这个操作一次,将会很影响性能,为了避免这样,vector和string通常会分配比新的空间需求更大的内存空间

十五、capacity操作让容器在不扩张内存空间的情况下可以容纳多少个元素,reserve操作通知容器它应该准备保存多少个元素, 只有当需要的内存空间超过当前容量时,reserve调用才会改变vector的容量,如果需求大小小于或等于当前容量,reserve什么也不做。当需求大小小于当前容量时,容器不会退回内存空间,因此在调用reserve后,capacity将会大于或等于传递给reserve的参数, 可以调用shrink_to_fit来要求deque、vector、string退回不需要的内存空间,但不保证一定会退回

十六、capacity和size区别:size是指它已经保存的元素的数量,capacity是指在不分配新的内存空间的前提下它最多可以保存多少元素

十七、当从一个const char*创建string时,指针指向的数组必须以空字符结尾,拷贝操作遇到空字符时停止,如果还传递给构造函数一个计数值,数组就不必以空字符结尾,如果未传递计数值也未以空字符结尾,或者给定计数值大于数组大小,则构造函数的行为是未定义的

十八、标准库定义了三个顺序容器适配器: stack、queue、priority_queue

十九、容器、迭代器和函数都由适配器,适配器不能构造在array上,也不能使用forward_list构造适配器

二十、stack要求push_back、pop_back金额back操作,因此可以使用除了array和forward_list外的任何容器类型构造; queue要求back、push_back、front、push_front,因此可以构造于list或deque上,但不能构造于vector上; priority_queue除了front、push_back和pop_back操作外还要求随机访问,因此可以构造于vector或deque上,但不能构造于list上

二一、queue默认基于deque实现,priority_queue默认基于vector实现; queue使用先进先出(FIFO)的存储和访问策略,priority_queue允许为队列中的元素建立优先级,新加入的元素会排在所有优先级比它低的已有元素的前面

 

list<string> authors = { "Milton", "Shakespeare", "Austen" };
	list<string>::iterator it1 = authors.begin(); // auto it1 = authors.begin();
	list<string>::reverse_iterator it2 = authors.rbegin();// 逆向迭代
	list<string>::const_iterator it3 = authors.cbegin();
	list<string>::const_reverse_iterator it4 = authors.crbegin();// 逆向迭代

	cout << *it1 << endl;
	cout << *it2 << endl;
	cout << *it3 << endl;
	cout << *it4 << endl;

	vector<const char*> articles = { "a", "an", "the" };
	list<string> list2(authors); // 拷贝赋值
	// 当传递迭代器参数来拷贝一个范围时, 就不要求容器类型是相同的了
	// 甚至新容器和原容器中的元素类型也可以不同,只要能将要拷贝的元素转换
	forward_list<string> words(articles.begin(), articles.end());
	

	list<string> names;
	// assign允许从一个不同但相容的类型赋值,或者从容器的一个子序列赋值
	// assign用参数所指定的元素替换左边容器中的所有元素
	names.assign(articles.cbegin(), articles.cend());

	list<string> slist1(1); // 1个元素,为空的string
	slist1.assign(10, "hi"); // 10个元素,每个都是hi

	vector<string> svec1(10, "hi");
	vector<string> svec2(15, "hello");  
	for (vector<string>::iterator str = svec1.begin(); str != svec1.end(); ++str) {
		cout << *str << endl;
	}
	for (vector<string>::iterator str = svec2.begin(); str != svec2.end(); ++str) {
		cout << *str << endl;
	}
	cout << "-----------" << endl;
	swap(svec1, svec2);
	for (vector<string>::iterator str = svec1.begin(); str != svec1.end(); ++str) {
		cout << *str << endl;
	}
	for (vector<string>::iterator str = svec2.begin(); str != svec2.end(); ++str) {
		cout << *str << endl;
	}

	// 在容器中访问元素的成员函数(front, back, at及下标),返回的都是引用
	cout << "-----------" << endl;
	auto front = svec1.front();
	auto back = svec1.back();
	cout << front << endl; // hello
	cout << back << endl;// hello
	front  = "world"; // front不是引用,它是back()的一个拷贝,所以并未改变svec1的元素
	auto front2 = svec1.front();
	auto& back2 = svec1.back();
	back2 = "china";
	auto back3 = svec1.back();
	cout << front << endl;// hello
	cout << back << endl;// hello
	cout << front2 << endl;// hello
	cout << back2 << endl;// china
	cout << back3 << endl;// china

	// 不能对内置数组类型进行拷贝或对象赋值,但array并无此限制
	int  digs[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	//int digsCopy[10] = digs;
	array<int, 10> digits = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	array<int, 10> digitsCopy = digits;



	list<int> ilist(10, 42);
	ilist.resize(15);
	ilist.resize(5, -1); // 5小于15,容器后的元素会被删除,初始值-1在这里没意义
	for (list<int>::iterator i = ilist.begin(); i != ilist.end(); ++i) {
		cout << *i << endl; 
	}

	ilist.clear();  // ilist.erase(ilist.begin(), ilist.end());
	

	cout << svec1.size() << endl; // size指它已经保存的元素的数量
	cout << svec1.capacity() << endl; //capacity是在不分配新的内存空间的前提下它最多可以保存多少元素

	stack<int> intStack;
	for (size_t x = 0; x != 10; ++x) {
		intStack.push(x);
	}
	while (!intStack.empty()) {
		intStack.pop();
	}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值