C++ STL 中 deque::operator= 和 deque::operator[] 之间的区别2025年5月13日 | 阅读17分钟 标准模板库(STL)是现代C++软件开发的核心部分,它提供了一组强大、有用、通用的数据结构来简化开发。在各种STL容器中,std::deque(双端队列的缩写)是一种特别高效且灵活的序列元素处理方式。std::deque是一个快速随机访问的数据结构,它提供动态重排大小以及在两端高效插入和删除元素的能力,使其兼具vector和链表的特点。结合这些特性,它成为寻求速度和灵活性的开发者的必备选择。 与deque相关的两个关键运算符是deque的赋值运算符(operator=)和下标运算符(operator[])。这两个运算符在实现的功能上大相径庭,但它们都是有效使用deque所必需的。在本文中,我们将讨论C++中deque::operator=和deque::operator[]之间的区别。在讨论它们的区别之前,我们必须先了解C++中的std::deque。 什么是std::deque?在深入探讨这些运算符的具体细节之前,了解std::deque的结构和行为会很有帮助。与std::vector不同,std::deque将其元素组织成一个分段结构,由固定大小的块链接在一起。std::vector将元素存储在一整块连续内存中。std::deque允许在两端高效地增长,而无需进行大规模的内存重新分配。其结果是一个容器,提供了
运算符在std::deque中的作用STL容器包含运算符,它们具有精妙的语法和自动行为。 deque::operator=(赋值运算符): 用于为deque分配新内容。它支持
deque::operator[] (下标运算符): 允许deque提供对其元素的随机访问。它以类似数组的直观语法读取或修改一个元素。 这些运算符的必要性?C++ STL 的 std::deque 容器是一个方便的数据结构,当我们需要动态调整数据大小、在数据两端快速插入或删除,或执行随机访问时非常有用。其功能基于两个基本运算符(operator=、operator[]),它们决定和访问容器数据。
1. deque::operator=在C++标准模板库(STL)中,std::deque的赋值运算符(operator=)为我们提供了一种简单高效的方式来更改deque容器的内容。它是用新元素重新分配deque的必备运算符,但会完全替换其当前内容,并确保容器根据新数据更新其大小。 deque::operator=让我们能够毫无问题地将一个deque的内容复制到另一个deque。deque还支持移动语义,它允许通过复制一个deque的资源到另一个deque来高效地重新分配,而无需额外复制底层数据。特别是在性能要求极高的应用程序中,避免不必要的复制变得至关重要。 除了复制和移动之外,这两种形式还接受来自初始化列表的赋值,该功能在C++11中引入。它允许以非常简短易读的方式从预定义值的列表中声明性地构造deque,摆脱了使用现代C++对容器进行常规重新分配的麻烦,使过程更加简洁。 deque::operator= 的复杂性很重要,因为它允许高效地处理各种赋值场景。该运算符提供了一种简单灵活的方式,可以将一个std::deque的内容复制或转移到另一个std::deque。由于它是处理容器的基本操作,因此理解deque::operator= 对于在实际编程任务中有效使用std::deque至关重要。 std::deque中的复制赋值std::deque的复制赋值运算符在另一个deque中创建一个deque内容的独立副本。该运算符会将源deque的所有元素分配给目标deque,这意味着目标deque将包含与源deque相同的元素,顺序也相同。操作后,目标deque的内容将被覆盖,大小等于源deque,deque将调整为包含该数量的元素。 关键属性
用例
std::deque中的移动赋值在std::deque的移动赋值运算符中,一个deque可以将其中元素的“内容”转移到另一个deque。它将资源从源deque移动到目标deque,因此在操作后,原始源deque处于有效但未指定的(unspecified)状态。它通常是空的,但请注意,这是未指定的。 关键属性
用例移动赋值最适合以下情况
std::deque中的初始化列表赋值可以使用初始化列表({}中的值列表)赋值运算符为deque赋值。C++11引入了这个运算符,它使得对deque的赋值或修改更加简洁易读。 关键属性
用例初始化列表赋值最适合以下情况
std::deque中的自赋值检查自赋值检查发生在deque被赋值给自己(源deque和目标deque是同一个对象)的任何时候。一般来说,将deque赋值给自己不会改变其内容或状态;一切都与之前相同。但是,赋值运算符必须小心处理自赋值,以免产生额外工作或导致错误。 关键属性
用例自赋值是某些情况下需要考虑的边缘情况
这些赋值操作中的每一种,如复制、移动、初始化列表等,都针对特定的情况和用法进行了优化。了解何时以及如何使用每个运算符可以大大提高管理std::deque容器的代码效率和正确性。 程序让我们通过一个示例来说明C++中的std::deque::operator=。 输出 The initial state of deque1 and deque2 before Copy Assignment: deque1 = { 1 2 3 4 5 } (Size: 5) deque2 = { } (Size: 0) After Copy Assignment: deque1 = { 1 2 3 4 5 } (Size: 5) deque2 = { 1 2 3 4 5 } (Size: 5) After modifying deque2 to ensure independence: deque1 = { 1 2 3 4 5 } (Size: 5) deque2 = { 99 2 3 4 5 } (Size: 5) Initial state of deque3 and deque4 before Move Assignment: deque3 = { 6 7 8 9 10 } (Size: 5) deque4 = { } (Size: 0) After Move Assignment: deque3 = { } (Size: 0) deque4 = { 6 7 8 9 10 } (Size: 5) State of deque5 before Initializer List Assignment: deque5 = { } (Size: 0) After Initializer List Assignment: deque5 = { 11 12 13 14 15 } (Size: 5) After Re-assigning using Initializer List: deque5 = { 16 17 18 } (Size: 3) State of deque6 before Self-Assignment: deque6 = { 19 20 21 } (Size: 3) After Self-Assignment: deque6 = { 19 20 21 } (Size: 3) Initial state of deque7 and deque8 before Combining Operations: deque7 = { 22 23 24 25 } (Size: 4) deque8 = { } (Size: 0) After Copy Assignment (deque8 = deque7): deque7 = { 22 23 24 25 } (Size: 4) deque8 = { 22 23 24 25 } (Size: 4) After Move Assignment (deque7 = std::move(deque8)): deque7 = { 22 23 24 25 } (Size: 4) deque8 = { } (Size: 0) After Initializer List Assignment (deque8 = {26, 27, 28}): deque8 = { 26 27 28 } (Size: 3) Final State of deque7 and deque8: deque7 = { 22 23 24 25 } (Size: 4) deque8 = { 26 27 28 } (Size: 3) 解释在第一部分,程序展示了将deque1的内容复制到deque2的复制赋值。deque1最初包含5个元素,而deque2为空。当deque2被复制赋值(deque2 = deque1)时,deque1的内容被简单地复制到deque2。但是,这意味着我们可以对deque2 = deque1执行操作,然后从任何点修改deque1;这不会改变deque2,因为它是独立的副本。程序验证了这种独立性,它修改了deque2中的一个元素;同时,deque1保持不变,这表明deque是相互独立的。 之后,程序展示了资源移动赋值。在这里,我们用元素填充deque3,但deque4最初是空的。当使用移动赋值(deque4 = std::move(deque3))将deque3的内容分配给deque4时,deque3中元素的归属权被转移到deque4。移动后,deque3变为空,因为它不再拥有这些元素。复制赋值非常低效。它们必须复制所有元素,而不是转移deque的所有内部资源(例如,内存指针)。 我们在程序中使用初始化列表赋值来为deque5分配一组新元素,然后展示初始化列表赋值。初始化列表(此处提供的元素列表)被提供给赋值(deque5 = {11, 12, 13, 14, 15}),并用其包含的元素替换deque5的原始内容。通过这种方式,如果事先知道要插入的确切值,我们可以轻松高效地为deque分配一组新值。 最后,程序展示了一个自赋值检查的例子,其中一个deque被赋值给自己。此操作可以防止在deque被赋值给自己时发生不必要的卡顿(确保不会发生重新分配或元素复制)。但是,实际上,赋值运算符会高效地处理自赋值,并且与标签不同,自赋值不会改变deque的状态。 2. deque::operator[]在std::deque::operator[]中,deque可以在给定索引处提供对其元素的直接、高效访问。Std::deque,一个双端队列,是一个序列容器,它可以在两端高效地插入和删除元素。当我们需要频繁地从前端或后端添加或删除元素时,它非常有用。operator[]是std::deque容器的一个关键特性,它允许开发人员明智地访问和修改容器内任意位置的元素。 就像数组索引运算符一样,它也是一个类似的运算符,允许对deque元素进行随机访问。它“返回”指定索引处的元素,这意味着它可以读写。提供的索引必须是有效索引,即它必须在0到deque.size() - 1之间。operator[]不像at()这样的其他成员函数那样执行边界检查,但与operator[]不同的是,这里进行的边界检查仅针对数组。它比以前更快,但在此过程中,如果使用无效索引,也会带来未定义行为的风险。 换句话说,通过索引访问deque中的任何元素都需要常数时间O(1),因此非常快。deque以块的形式存储其元素,并且该运算符允许直接访问这些块,确保快速访问,而与deque的大小无关。但是,没有自动检查越界访问,因此您需要确保索引在边界内。如果要安全地检查越界访问,应使用deque::at()方法,该方法会执行边界检查。 程序让我们通过一个示例来说明C++中的std::deque::operator[]。 输出 Original deque1: deque1[0] = 10 deque1[1] = 20 deque1[2] = 30 deque1[3] = 40 deque1[4] = 50 After modifying deque1[2]: deque1[0] = 10 deque1[1] = 20 deque1[2] = 35 deque1[3] = 40 deque1[4] = 50 Trying to access deque1[10] (out of bounds): Const deque2 elements: deque2[0] = 100 deque2[1] = 200 deque2[2] = 300 deque2[3] = 400 deque2[4] = 500 Modified deque3: deque3[0] = 10 deque3[1] = 2 deque3[2] = 3 deque3[3] = 4 deque3[4] = 50 Large deque, first 5 elements: largeDeque[0] = 0 largeDeque[1] = 10 largeDeque[2] = 20 largeDeque[3] = 30 largeDeque[4] = 40 All elements of largeDeque (first 10 shown for brevity): largeDeque[0] = 0 largeDeque[1] = 10 largeDeque[2] = 20 largeDeque[3] = 30 largeDeque[4] = 40 largeDeque[5] = 50 largeDeque[6] = 60 largeDeque[7] = 70 largeDeque[8] = 80 largeDeque[9] = 90 Modified last element of largeDeque: largeDeque[999] = 10000 Nested deque (accessing elements): nestedDeque[1][2] = 6 After modification, nestedDeque[0][1] = 20 解释
std::deque::operator[] 和 std::deque::operator= 在 C++ 中的主要区别std::deque::operator[] 和 std::deque::operator= 在 C++ 中有几个主要区别。一些主要区别如下:
|
我们请求您订阅我们的新闻通讯以获取最新更新。