C++初阶之一篇文章让你掌握vector(理解和使用)(下)

简介: 2.4 vector元素访问函数2.4.1 operator[]reference operator[] (size_type n);const_reference operator[] (size_type n) const;

2.4 vector元素访问函数

2.4.1 operator[]

reference operator[] (size_type n);

const_reference operator[] (size_type n) const;

std::vector 提供了两个重载的 operator[],用于通过索引访问容器中的元素:

reference operator[] (size_type n): 这个重载用于访问 std::vector 中指定索引位置 n 处的元素,并返回该元素的引用。通过此重载,可以对元素进行读取和修改操作。如果 n 超出了有效的索引范围(即小于 0 或大于等于 size()),行为将是未定义的(Undefined Behavior)。

const_reference operator[] (size_type n) const;: 这个重载是 const 成员函数,用于在 const 修饰的 std::vector 对象中访问指定索引位置 n 处的元素,并返回该元素的常量引用。通过此重载,只能对元素进行读取操作,不能修改元素的值

下面是一个使用 operator[] 的示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> myVector = {1, 2, 3, 4, 5};
    // 修改元素
    myVector[2] = 10; // 将索引 2 处的元素修改为 10
    // 读取元素
    std::cout << "Element at index 2: " << myVector[2] << std::endl;
    // 使用 const_reference 进行读取(适用于 const std::vector)
    const std::vector<int>& constVector = myVector;
    std::cout << "Const element at index 4: " << constVector[4] << std::endl;
    return 0;
}

输出结果:

Element at index 2: 10
Const element at index 4: 5

需要注意的是,通过 operator[] 访问元素时,应该确保索引在有效的范围内,否则可能导致未定义的行为。通常情况下,应该在访问元素之前使用 size() 函数检查索引的有效性。

2.4.2 at

reference at (size_type n);

const_reference at (size_type n) const;

std::vector 提供了两个重载的 at 函数,用于通过索引访问容器中的元素:

reference at (size_type n): 这个重载用于访问 std::vector 中指定索引位置 n 处的元素,并返回该元素的引用。通过此重载,可以对元素进行读取和修改操作。如果 n 超出了有效的索引范围(即小于 0 或大于等于 size()),at 函数会抛出 std::out_of_range 异常,以保证程序的健壮性。

const_reference at (size_type n) const;: 这个重载是 const 成员函数,用于在 const 修饰的 std::vector 对象中访问指定索引位置 n 处的元素,并返回该元素的常量引用。通过此重载,只能对元素进行读取操作,不能修改元素的值

下面是一个使用 at 函数的示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> myVector = {1, 2, 3, 4, 5};
    // 修改元素
    myVector.at(2) = 10; // 将索引 2 处的元素修改为 10
    // 读取元素
    std::cout << "Element at index 3: " << myVector.at(3) << std::endl;
    // 使用 const_reference 进行读取(适用于 const std::vector)
    const std::vector<int>& constVector = myVector;
    std::cout << "Const element at index 4: " << constVector.at(4) << std::endl;
    // 尝试访问越界的索引(会抛出 std::out_of_range 异常)
    try {
        int value = myVector.at(10);
    } catch (const std::out_of_range& e) {
        std::cout << "Caught exception: " << e.what() << std::endl;
    }
    return 0;
}

输出结果:

Element at index 3: 4
Const element at index 4: 5
Caught exception: vector::_M_range_check: __n (which is 10) >= this->size() (which is 5)

可以看到,在尝试访问越界的索引时,at 函数抛出了 std::out_of_range 异常,提醒我们访问索引越界了。因此,相比于 operator[],使用 at 函数可以更加安全地访问元素,特别是在不确定索引是否有效时。

2.4.3 front()

reference front();

const_reference front() const;

std::vector 提供了两个重载的 front 函数,用于访问容器中的第一个元素:

reference front(): 这个重载用于访问 std::vector 容器中的第一个元素,并返回该元素的引用。通过此重载,可以对元素进行读取和修改操作。

const_reference front() const;: 这个重载是 const 成员函数,用于在 const 修饰的 std::vector 对象中访问第一个元素,并返回该元素的常量引用。通过此重载,只能对元素进行读取操作,不能修改元素的值

下面是一个使用 front 函数的示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> myVector = {1, 2, 3, 4, 5};
    // 修改第一个元素
    myVector.front() = 10; // 将第一个元素修改为 10
    // 读取第一个元素
    std::cout << "First element: " << myVector.front() << std::endl;
    // 使用 const_reference 进行读取(适用于 const std::vector)
    const std::vector<int>& constVector = myVector;
    std::cout << "Const first element: " << constVector.front() << std::endl;
    return 0;
}

输出结果:

First element: 10
Const first element: 10

可以看到,front 函数用于获取 std::vector 容器中的第一个元素,并且可以通过不同的重载版本来实现读取和修改操作,适用于常规和 const 对象。

2.4.4 back()

reference back();

const_reference back() const;

std::vector 提供了两个重载的 back 函数,用于访问容器中的最后一个元素:

reference back(): 这个重载用于访问 std::vector 容器中的最后一个元素,并返回该元素的引用。通过此重载,可以对元素进行读取和修改操作。

const_reference back() const;: 这个重载是 const 成员函数,用于在 const 修饰的 std::vector 对象中访问最后一个元素,并返回该元素的常量引用。通过此重载,只能对元素进行读取操作,不能修改元素的值

下面是一个使用 back 函数的示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> myVector = {1, 2, 3, 4, 5};
    // 修改最后一个元素
    myVector.back() = 10; // 将最后一个元素修改为 10
    // 读取最后一个元素
    std::cout << "Last element: " << myVector.back() << std::endl;
    // 使用 const_reference 进行读取(适用于 const std::vector)
    const std::vector<int>& constVector = myVector;
    std::cout << "Const last element: " << constVector.back() << std::endl;
    return 0;
}

输出结果:

Last element: 10
Const last element: 10

可以看到,back 函数用于获取 std::vector 容器中的最后一个元素,并且可以通过不同的重载版本来实现读取和修改操作,适用于常规和 const 对象。

2.4.5 data() C++11

value_type* data() noexcept;

const value_type* data() const noexcept;

std::vector 提供了两个重载的 data 函数,用于访问底层存储数组的指针:

value_type* data() noexcept;: 这个重载返回一个指向 std::vector 容器底层存储数组的非常量指针。通过此重载,可以对底层数组进行读写操作。noexcept 表示这个函数不会抛出异常

const value_type* data() const noexcept;: 这个重载是 const 成员函数,返回一个指向 std::vector 容器底层存储数组的常量指针。通过此重载,只能对底层数组进行读取操作,不能修改数组的内容。noexcept 表示这个函数不会抛出异常。

下面是一个使用 data 函数的示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> myVector = {1, 2, 3, 4, 5};
    // 获取底层存储数组的非常量指针
    int* ptr = myVector.data();
    // 修改底层存储数组
    ptr[0] = 10;
    ptr[3] = 20;
    // 通过 const value_type* 获取底层存储数组的常量指针(适用于 const std::vector)
    const std::vector<int>& constVector = myVector;
    const int* constPtr = constVector.data();
    // 读取底层存储数组
    for (size_t i = 0; i < myVector.size(); ++i) {
        std::cout << "Element " << i << ": " << constPtr[i] << std::endl;
    }
    return 0;
}

输出结果:

Element 0: 10
Element 1: 2
Element 2: 3
Element 3: 20
Element 4: 5

可以看到,data 函数用于获取 std::vector 容器底层存储数组的指针,并可以通过不同的重载版本来实现读取和修改操作,适用于常规和 const 对象。需要注意的是,在使用 data 函数时,需要确保 std::vector 不为空,否则获取到的指针可能是空指针。

2.5 vector增删查改函数

2.5.1 assign()

template <class InputIterator> void assign (InputIterator first, InputIterator last);

void assign (size_type n, const value_type& val);

std::vector 提供了两种重载的 assign 函数,用于替换 std::vector 中的内容:

template <class InputIterator> void assign (InputIterator first, InputIterator last);:

  1. 这个重载模板函数接受一对迭代器 first 和 last 作为参数,它们表示一个范围。assign 函数将容器的内容替换为指定范围中的元素
  2. 这个重载函数可以接受任何类型的迭代器,例如指针,普通迭代器或者 const 迭代器等。只要指定的范围是有效的,assign 函数就会将容器的内容替换为该范围内的元素

void assign (size_type n, const value_type& val);:

  1. 这个重载函数接受一个整数 n 和一个值 val 作为参数,将容器的内容替换为 n 个 val 值
  2. n 表示新容器应该包含多少个 val 值,容器的大小将设置为 n,原来的元素都将被替换

下面是使用 assign 函数的示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> myVector;
    // 使用 assign 重载函数替换容器内容为一组元素
    std::vector<int> sourceVector = {10, 20, 30, 40, 50};
    myVector.assign(sourceVector.begin(), sourceVector.end());
    // 打印容器内容
    for (int num : myVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    // 使用 assign 重载函数替换容器内容为多个相同的元素
    myVector.assign(5, 100);
    // 打印容器内容
    for (int num : myVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    return 0;
}

输出结果:

10 20 30 40 50 
100 100 100 100 100

在这个示例中,我们首先使用 assign 函数将 myVector 容器的内容替换为另一个向量 sourceVector 中的元素。然后,我们再次使用 assign 函数将 myVector 的内容替换为 5 个值为 100 的元素。

2.5.2 push_back()

void push_back (const value_type& val);

std::vector 的 push_back 函数用于向容器的末尾添加一个新元素。它接受一个引用参数 const value_type& val,表示要添加的新元素的值。

其中,value_type 是 std::vector 中元素的类型,通常是模板参数中指定的类型。const 表示在函数中不会修改传入的值。

下面是一个使用 push_back 函数的示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> myVector;
    myVector.push_back(10);
    myVector.push_back(20);
    myVector.push_back(30);
    // 打印容器内容
    for (int num : myVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    return 0;
}

输出结果:

10 20 30

2.5.3 pop_back()

void pop_back();

std::vector 的 pop_back 函数用于从容器的末尾移除一个元素。它没有参数,直接从容器的末尾删除最后一个元素。

下面是一个使用 pop_back 函数的示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> myVector = {10, 20, 30};
    std::cout << "Before pop_back: ";
    for (int num : myVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    myVector.pop_back();
    std::cout << "After pop_back: ";
    for (int num : myVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    return 0;
}

输出结果:

Before pop_back: 10 20 30 
After pop_back: 10 20

在这个示例中,我们创建了一个 std::vector 容器,并初始化了三个元素:10、20 和 30。然后,我们使用 pop_back 函数将最后一个元素 30 从容器中移除。

2.5.4 insert()

iterator insert (iterator position, const value_type& val);

void insert (iterator position, size_type n, const value_type& val);

template <class InputIterator> void insert (iterator position, InputIterator first, InputIterator last);

std::vector 的 insert 函数用于在指定位置插入一个或多个元素。

1. 插入单个元素

这个版本的 insert 函数在 position 位置之前插入一个元素,并返回指向插入元素的迭代器。

示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> myVector = {1, 2, 3};
    std::vector<int>::iterator it = myVector.begin() + 1;
    myVector.insert(it, 100);
    for (int num : myVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    return 0;
}

输出结果:

1 100 2 3

2. 插入多个相同元素:

这个版本的 insert 函数在 position 位置之前插入 n 个值为 val 的元素。

示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> myVector = {1, 2, 3};
    std::vector<int>::iterator it = myVector.begin() + 1;
    myVector.insert(it, 3, 100);
    for (int num : myVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    return 0;
}

输出结果:

1 100 100 100 2 3

3. 插入范围内的元素:

这个版本的 insert 函数在 position 位置之前插入从 first 到 last 区间内的元素。

示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> myVector = {1, 2, 3};
    std::vector<int> toInsert = {100, 200};
    std::vector<int>::iterator it = myVector.begin() + 1;
    myVector.insert(it, toInsert.begin(), toInsert.end());
    for (int num : myVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    return 0;
}

输出结果:

1 100 200 2 3

在所有的 insert 函数中,参数 position 是一个迭代器,用于指定要插入的位置。这些函数将在 position 之前插入元素,并调整容器中的其他元素。

2.5.5 erase()

iterator erase (iterator position);

iterator erase (iterator first, iterator last);

std::vector 的 erase 函数用于从容器中移除一个或一段元素。

1.移除单个元素:

这个版本的 erase 函数移除由 position 指向的元素,并返回指向下一个元素的迭代器。

示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> myVector = {1, 2, 3, 4, 5};
    std::vector<int>::iterator it = myVector.begin() + 2;
    myVector.erase(it);
    for (int num : myVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    return 0;
}

输出结果:

1 2 4 5

2. 移除一段元素

这个版本的 erase 函数移除从 first 到 last 区间内的元素,并返回指向下一个元素的迭代器。

示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> myVector = {1, 2, 3, 4, 5};
    std::vector<int>::iterator it1 = myVector.begin() + 1;
    std::vector<int>::iterator it2 = myVector.begin() + 3;
    myVector.erase(it1, it2);
    for (int num : myVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    return 0;
}

输出结果:

1 4 5

在所有的 erase 函数中,参数可以是迭代器或者是一个表示范围的迭代器对,用于指定要移除的元素的位置。这些函数将移除指定的元素,并调整容器中的其他元素。移除后,迭代器失效,需小心使用。

2.5.6 swap()

void swap (vector& x);

std::vector 的 swap 函数用于交换两个顺序表的内容。它将当前顺序表和参数顺序表 x 的内容进行交换。

示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> vec1 = {1, 2, 3};
    std::vector<int> vec2 = {4, 5, 6};
    std::cout << "Before swap:" << std::endl;
    for (int num : vec1) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    for (int num : vec2) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    vec1.swap(vec2);
    std::cout << "After swap:" << std::endl;
    for (int num : vec1) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    for (int num : vec2) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    return 0;
}

输出结果:

Before swap:
1 2 3 
4 5 6 
After swap:
4 5 6 
1 2 3

注意swap 函数交换的是容器的内容,并不交换容器的容量大小,所以在交换后,两个容器的容量仍然保持不变。这可能导致在交换后,某个容器的容量比实际元素个数多,从而导致额外的内存分配和浪费。如果你想要交换两个容器的容量大小,可以使用 std::vector 的移动构造函数和移动赋值运算符

2.5.7 clear()

void clear();

clear 函数用于清空 std::vector 中的所有元素,使其变为空顺序表。

示例:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::cout << "Before clear:" << std::endl;
    for (int num : vec) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    vec.clear();
    std::cout << "After clear:" << std::endl;
    for (int num : vec) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    return 0;
}

输出结果:

Before clear:
1 2 3 4 5 
After clear:

注意clear 函数只清空容器中的元素,并不改变容器的容量大小。如果你想要同时清空元素并释放内存,可以使用 std::vector 的 shrink_to_fit 函数。

2.5.8 emplace() C++11

template <class... Args> iterator emplace (const_iterator position, Args&&... args);

emplace 函数是 std::vector 的一个成员函数,用于在指定位置插入一个新元素,并通过传递参数来构造该元素。它支持在容器中的指定位置就地构造元素,而不需要额外的拷贝或移动操作。

参数说明:

position: 指定插入位置的迭代器,表示新元素将插入到该位置之前。

args: 可变参数模板,表示新元素的构造参数。

返回值:

返回一个迭代器,指向插入的新元素。

示例:

#include <iostream>
#include <vector>
class Person {
public:
    Person(const std::string& name, int age) : name_(name), age_(age) {
        std::cout << "Constructing " << name_ << ", Age: " << age_ << std::endl;
    }
private:
    std::string name_;
    int age_;
};
int main() {
    std::vector<Person> people;
    // 使用 emplace 在位置 0 处插入新元素
    people.emplace(people.begin(), "zhangsan", 25);
    people.emplace(people.begin() + 1, "lisi", 30);
    return 0;
}

输出结果:

Constructing zhangsan, Age: 25
Constructing lisi, Age: 30

在上述示例中,emplace 函数会在指定位置就地构造 Person 类的对象,并通过传递参数 “zhangsan” 和 25 构造了一个名为 zhangsan,年龄为 25 的 Person 对象。然后又在位置 1 处就地构造了一个名为 lisi,年龄为 30 的 Person 对象。由于 emplace 避免了拷贝或移动操作,因此效率较高。

2.5.9 emplace_back() C++11

template <class... Args> void emplace_back (Args&&... args);

emplace_back 是 std::vector 的一个成员函数,用于在容器的末尾就地构造一个新元素,并通过传递参数来构造该元素。

示例:

#include <iostream>
#include <vector>
class Person {
public:
    Person(const std::string& name, int age) : name_(name), age_(age) {
        std::cout << "Constructing " << name_ << ", Age: " << age_ << std::endl;
    }
private:
    std::string name_;
    int age_;
};
int main() {
    std::vector<Person> people;
    // 使用 emplace_back 在容器末尾插入新元素
    people.emplace_back("zhangsan", 25);
    people.emplace_back("lisi", 30);
    return 0;
}

输出结果:

Constructing zhangsan, Age: 25
Constructing lisi, Age: 30

在上述示例中,emplace_back 函数会在 people 容器的末尾就地构造 Person 类的对象,并通过传递参数构造了一个名为 zhangsan,年龄为 25 的 Person 对象,然后又构造了一个名为 lisi,年龄为 30 的 Person 对象。由于 emplace_back 避免了拷贝或移动操作,因此效率较高。

2.5.10 vector 迭代器失效问题

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)。

对于vector可能会导致其迭代器失效的操作有:

1. 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等。

#include <iostream>
using namespace std;
#include <vector>
int main()
{
  vector<int> v{ 1,2,3,4,5,6 };
  auto it = v.begin();
  v.assign(100, 8);
  while (it != v.end())
  {
    cout << *it << " ";
    ++it;
  }
  cout << endl;
  return 0;
}

出错原因:

以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的空间,而引起代码运行时崩溃。

解决方式:

在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新赋值即可。

2. 指定位置元素的删除操作–erase

#include <iostream>
using namespace std;
#include <vector>
int main()
{
  int a[] = { 1, 2, 3, 4 };
  vector<int> v(a, a + sizeof(a) / sizeof(int));
  // 使用find查找3所在位置的iterator
  vector<int>::iterator pos = find(v.begin(), v.end(), 3);
  // 删除pos位置的数据,导致pos迭代器失效。
  v.erase(pos);
  cout << *pos << endl; // 此处会导致非法访问
  return 0;
}

erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。

3.删除vector中所有的偶数

迭代器失效:

int main()
{
  vector<int> v{ 1, 2, 3, 4 };
  auto it = v.begin();
  while (it != v.end())
  {
    if (*it % 2 == 0)
      v.erase(it);
    ++it;
  }
  return 0;
}

正确调用:

int main()
{
  vector<int> v{ 1, 2, 3, 4 };
  auto it = v.begin();
  while (it != v.end())
  {
    if (*it % 2 == 0)
      it = v.erase(it);
    else
      ++it;
  }
  return 0;
}

4.Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端。

//扩容之后,迭代器已经失效了,程序虽然可以运行,但是运行结果已经不对了
int main()
{
  vector<int> v{ 1,2,3,4,5 };
  for (size_t i = 0; i < v.size(); ++i)
    cout << v[i] << " ";
  cout << endl;
  auto it = v.begin();
  cout << "扩容之前,vector的容量为: " << v.capacity() << endl;
  // 通过reserve将底层空间设置为100,目的是为了让vector的迭代器失效
  v.reserve(100);
  cout << "扩容之后,vector的容量为: " << v.capacity() << endl;
  // 经过上述reserve之后,it迭代器肯定会失效,在vs下程序就直接崩溃了,但是linux下不会
  // 虽然可能运行,但是输出的结果是不对的
  while (it != v.end())
  {
    cout << *it << " ";
    ++it;
  }
  cout << endl;
  return 0;
}

程序输出:

1 2 3 4 5
扩容之前,vector的容量为: 5
扩容之后,vector的容量为 : 100
0 2 3 4 5 409 1 2 3 4 5
//erase删除任意位置代码后,linux下迭代器并没有失效
// 因为空间还是原来的空间,后序元素往前搬移了,it的位置还是有效的
#include <vector>
#include <algorithm>
int main()
{
  vector<int> v{ 1,2,3,4,5 };
  vector<int>::iterator it = find(v.begin(), v.end(), 3);
  v.erase(it);
  cout << *it << endl;
  while (it != v.end())
  {
    cout << *it << " ";
    ++it;
  }
  cout << endl;
  return 0;
}

程序可以正常运行,并打印:

4 4
5
// erase删除的迭代器如果是最后一个元素,删除之后it已经超过end
// 此时迭代器是无效的,++it导致程序崩溃
int main()
{
  vector<int> v{ 1,2,3,4,5 };
  // vector<int> v{1,2,3,4,5,6};
  auto it = v.begin();
  while (it != v.end())
  {
    if (*it % 2 == 0)
      v.erase(it);
    ++it;
  }
  for (auto e : v)
    cout << e << " ";
  cout << endl;
  return 0;
}

使用第一组数据时,程序可以运行

[kingxzq@localhost]$ g++ testVector.cpp - std = c++11
[kingxzq@localhost]$ . / a.out
1 3 5

使用第二组数据时,程序最终会崩溃

[kingxzq@localhost]$ vim testVector.cpp
[kingxzq@localhost]$ g++ testVector.cpp - std = c++11
[kingxzq@localhost]$ . / a.out
Segmentation fault

从上述三个例子中可以看到:SGI STL中,迭代器失效后,代码并不一定会崩溃,但是运行结果肯定不对,如果it不在begin和end范围内,肯定会崩溃的。

5. 与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效

#include <string>
void TestString()
{
  string s("hello");
  auto it = s.begin();
  // 放开之后代码会崩溃,因为resize到20会string会进行扩容
  // 扩容之后,it指向之前旧空间已经被释放了,该迭代器就失效了
  // 后序打印时,再访问it指向的空间程序就会崩溃
  //s.resize(20, '!');
  while (it != s.end())
  {
    cout << *it;
    ++it;
  }
  cout << endl;
  it = s.begin();
  while (it != s.end())
  {
    it = s.erase(it);
    // 按照下面方式写,运行时程序会崩溃,因为erase(it)之后
    // it位置的迭代器就失效了
    // s.erase(it);
    ++it;
  }
}

迭代器失效解决办法:在使用前,对迭代器重新赋值即可。

结语

有兴趣的小伙伴可以关注作者,如果觉得内容不错,请给个一键三连吧,蟹蟹你哟!!!

制作不易,如有不正之处敬请指出

感谢大家的来访,UU们的观看是我坚持下去的动力

在时间的催化剂下,让我们彼此都成为更优秀的人吧!!!

相关文章
|
4月前
|
存储 编译器 C++
【C++】vector介绍+模拟实现
【C++】vector介绍+模拟实现
|
28天前
|
存储 编译器 C语言
【c++丨STL】vector的使用
本文介绍了C++ STL中的`vector`容器,包括其基本概念、主要接口及其使用方法。`vector`是一种动态数组,能够根据需要自动调整大小,提供了丰富的操作接口,如增删查改等。文章详细解释了`vector`的构造函数、赋值运算符、容量接口、迭代器接口、元素访问接口以及一些常用的增删操作函数。最后,还展示了如何使用`vector`创建字符串数组,体现了`vector`在实际编程中的灵活性和实用性。
54 4
|
10天前
|
存储 对象存储 C++
C++ 中 std::array<int, array_size> 与 std::vector<int> 的深入对比
本文深入对比了 C++ 标准库中的 `std::array` 和 `std::vector`,从内存管理、性能、功能特性、使用场景等方面详细分析了两者的差异。`std::array` 适合固定大小的数据和高性能需求,而 `std::vector` 则提供了动态调整大小的灵活性,适用于数据量不确定或需要频繁操作的场景。选择合适的容器可以提高代码的效率和可靠性。
31 0
|
14天前
|
存储 编译器 C语言
【c++丨STL】vector模拟实现
本文深入探讨了 `vector` 的底层实现原理,并尝试模拟实现其结构及常用接口。首先介绍了 `vector` 的底层是动态顺序表,使用三个迭代器(指针)来维护数组,分别为 `start`、`finish` 和 `end_of_storage`。接着详细讲解了如何实现 `vector` 的各种构造函数、析构函数、容量接口、迭代器接口、插入和删除操作等。最后提供了完整的模拟实现代码,帮助读者更好地理解和掌握 `vector` 的实现细节。
27 0
|
2月前
|
存储 C++ 索引
【C++打怪之路Lv9】-- vector
【C++打怪之路Lv9】-- vector
26 1
|
2月前
|
安全 测试技术 C++
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化2
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化
77 6
|
2月前
|
安全 测试技术 C++
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化1
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化
93 7
|
2月前
|
编译器 C++
【C++】—— vector模拟实现
【C++】—— vector模拟实现
|
2月前
|
编译器 C语言 C++
【C++篇】解密 STL 动态之魂:全面掌握 C++ vector 的高效与优雅
【C++篇】解密 STL 动态之魂:全面掌握 C++ vector 的高效与优雅
62 3
|
2月前
|
C++
【C++】C++ STL探索:Vector使用与背后底层逻辑(三)
【C++】C++ STL探索:Vector使用与背后底层逻辑