文章目录

  • empty()和size()都可以判断容器是否为空,谁更好?
  • array 为何比 普通数组 安全
  • vector容器的 容量(capacity)和 大小(size)的区别
  • vector添加元素push_back()和emplace_back()的区别
  • vector插入元素insert()和emplace()的区别
  • vecter容器使用remove()需要注意:
  • 为何要避免使用vector
  • deque容器为什么没有提供data()成员函数 ( 为何不要使用指针去访问deque容器中指定位置处的元素 )
  • forward_list没有size()成员函数,如何获取元素个数


empty()和size()都可以判断容器是否为空,谁更好?

建议使用 empty() 成员方法。理由很简单,无论是哪种容器,只要其模板类中提供了 empty() 成员方法,使用此方法都可以保证在 O(1) 时间复杂度内完成对“容器是否为空”的判断;但对于 list 容器来说,使用 size() 成员方法判断“容器是否为空”,可能要消耗 O(n) 的时间复杂度。
此处参考

array 为何比 普通数组 安全

普通数组没有做任何边界检查,所以即便使用越界的索引值去访问或存储元素,也不会被检测到
例如:

int test2[10] = { 1,2,3,4,5 };
test2[4] = 2.0 * test2[111];
std::cout << test2[4] << std::endl;

注意:array 这样使用也不会被检测到

std::array<int, 10> test{ 1,2,3,4,5 };
test[4] = 2.0 * test[111];
std::cout << test[4] << std::endl;

为了能够有效地避免越界访问的情况,可以使用 array 容器提供的 at() 成员函数,例如 :

std::array<int, 10> test{ 1,2,3,4,5 };
test.at(4) = 2.0 * test.at(111);
std::cout << test[4] << std::endl;

vector容器的 容量(capacity)和 大小(size)的区别

vector 容器的容量(用 capacity 表示),指的是在不分配更多内存的情况下,容器可以保存的最多元素个数;而 vector 容器的大小(用 size 表示),指的是它实际所包含的元素个数。

如何排查容器假死的情况 对容器的检查方法_STL

注意: 当 vector 容器的大小和容量相等时,如果再向其添加(或者插入)一个元素,vector 往往会申请多个存储空间,而不仅仅只申请 1 个,并且容器会重新生成。
例:

int main() {
	std::vector<int>test{ 1,2,3,4,5 };
	std::cout << "test 容量是:" << test.capacity() << std::endl;
	std::cout << "test 大小是:" << test.size() << std::endl;
	std::cout << "test首地址:" <<test.data() << std::endl;

	test.push_back(6);

	std::cout << "test 容量是(2):" << test.capacity() << std::endl;
	std::cout << "test 大小是(2):" << test.size() << std::endl;
	std::cout << "test首地址:" << test.data() << std::endl;

	test.push_back(7);

	std::cout << "test 容量是(2):" << test.capacity() << std::endl;
	std::cout << "test 大小是(2):" << test.size() << std::endl;
	std::cout << "test首地址:" << test.data() << std::endl;
}

结果:

test 容量是:5
test 大小是:5
test首地址:00000237D6E200E0
test 容量是(2):7
test 大小是(2):6
test首地址:00000237D6E1D320
test 容量是(2):7
test 大小是(2):7
test首地址:00000237D6E1D320

可见当容器的大小超过容器的容量的时候,容器的首地址改变了,说明容器重建了

注意:当 vector 容器的大小和容量相等时,向其添加一个元素时会使得首地址发生改变。(经常有人因此发生bug,需要注意)

vector内部的扩容步骤:

  1. 弃用现有的内存空间,重新申请更大的内存空间;
  2. 将旧内存空间中的数据,按原有顺序移动到新的内存空间中;
  3. 将旧的内存空间释放。

vector添加元素push_back()和emplace_back()的区别

emplace_back() 和 push_back() 的区别,就在于底层实现的机制不同。push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。
简而言之:emplace_back()是c++11 新添加的,效率要比push_back()高

vector插入元素insert()和emplace()的区别

语法格式

用法说明

iterator insert(pos,elem)

在迭代器 pos 指定的位置之前插入一个新元素elem,并返回表示新插入元素位置的迭代器。

iterator insert(pos,n,elem)

在迭代器 pos 指定的位置之前插入 n 个元素 elem,并返回表示第一个新插入元素位置的迭代器。

iterator insert(pos,first,last)

在迭代器 pos 指定的位置之前,插入其他容器(不仅限于vector)中位于 [first,last) 区域的所有元素,并返回表示第一个新插入元素位置的迭代器。

iterator insert(pos,initlist)

在迭代器 pos 指定的位置之前,插入初始化列表(用大括号{}括起来的多个元素,中间有逗号隔开)中所有的元素,并返回表示第一个新插入元素位置的迭代器。

iterator emplace (pos, elem)

在迭代器 pos 指定的位置之前插入一个新元素elem,并返回表示新插入元素位置的迭代器。

可见:insert() 的功能要比 emplace()多。单比较插入一个元素,emplace()效率要比insert()要高,原因同上一条。

vecter容器使用remove()需要注意:

注意: remove()不是vector的成员函数,该函数定义在 头文件中

通过remove()函数删除 vector容器中的多个指定元素,该容器的大小和容量都没有改变,其剩余位置还保留了之前存储的元素,还需要使用 erase() 成员函数删掉这些无用的元素。

如图:

如何排查容器假死的情况 对容器的检查方法_STL_02


例子:

int main() {
	std::vector<int>test{ 9,9,9,1,2,3,4,5,6,7,8,9 };
	std::cout << "size:"<< test.size() << std::endl;
	std::cout << "capacity:" << test.capacity() << std::endl;

	for (auto i : test) {
		printf(" %d", i);
	}
	printf("\n");

	auto iter = std::remove(test.begin(), test.end(), 9);

	for (auto i : test) {
		printf(" %d", i);
	}
	printf("\n");
	std::cout << "size:" << test.size() << std::endl;
	std::cout << "capacity:" << test.capacity() << std::endl;

	test.erase(iter, test.end());
	for (auto i : test) {
		printf(" %d", i);
	}
	printf("\n");
	std::cout << "size:" << test.size() << std::endl;
	std::cout << "capacity:" << test.capacity() << std::endl;
}

结果:

size:12
capacity:12
 9 9 9 1 2 3 4 5 6 7 8 9
 1 2 3 4 5 6 7 8 6 7 8 9
size:12
capacity:12
 1 2 3 4 5 6 7 8
size:8
capacity:12

为何要避免使用vector

这是个c++ 的历史遗留问题,在早先vector每个元素是存在一个bit中而不是byte。
且vector 不完全满足 C++ 标准中对容器的要求,
所以vector就不是一个容器,尽量避免在实际场景中使用它!
在需要用到的时候用 deque 代替

deque容器为什么没有提供data()成员函数 ( 为何不要使用指针去访问deque容器中指定位置处的元素 )

和vector不同,deque的存储空间不一定是连续的,通常他是有多个连续的空间组成的。因此尝试使用指针去访问 deque 容器中指定位置处的元素,是非常危险的

deque的存储方式如下图所示:

如何排查容器假死的情况 对容器的检查方法_STL_03

forward_list没有size()成员函数,如何获取元素个数

可以使用头文件 中的 distance() 函数

#include <iostream>
#include <forward_list>
#include <iterator>
int main()
{
	std::forward_list<int> test{1,2,3,4,5,6,7,8,9,10};
	int size = std::distance(test.begin(), test.end());
	int size2 = std::distance(std::begin(test), std::end(test));
	std::cout << "size:" << size << std::endl;
	std::cout << "size2:" << size2 << std::endl;
}
size:10
size2:10