C++ std::vector删除元素的几种方式及区别

容器vector在删除过程中,常用的函数。

函数作用
pop_back()删除 vector 容器中最后一个元素,该容器的大小(size)会减 1,但容量(capacity)不会发生改变。
erase(iter)删除 vector 容器中iter迭代器指定位置处的元素,并返回指向被删除元素下一个位置元素的迭代器。该容器的大小(size)会减 1,但容量(capacity)不会发生改变。
erase(iter1,iter2)删除 vector 容器中位于迭代器 [iter1,iter2)指定区域内的所有元素,并返回指向被删除区域下一个位置元素的迭代器。该容器的大小(size)会减小,但容量(capacity)不会发生改变。
clear()删除 vector 容器中所有的元素,使其变成空的 vector 容器。该函数会改变 vector 的大小(变为 0),但不是改变其容量。
remove(iter1,iter2,key)删除容器中所有和指定元素值相等的元素,并返回指向最后一个元素下一个位置的迭代器。值得一提的是,调用该函数不会改变容器的大小和容量。(后面会详细说明,该函数的用法)
swap(vector)用于交换向量的内容,用一个向量调用它并接受另一个向量作为参数并交换它们的内容。 (两个向量的大小可能不同)。通过交换向量进行删除。
shrink_to_fit()将vector 容器的容量缩减至和实际存储元素的个数相等。可以配合以上的函数,收回内存

注意:

  • 容器的大小size:容器实际存放元素的数量。

  • 容器的容量capacity:容器在内存中开辟空间的容量(在不开辟新的空间时,能存放数据的大小)。

1. 使用earse和remove配合删除容器中指定值的元素

remove函数本质上其实并没有完成元素的完全删除工作,因为容器的大小都没有改变,它只是将所有被删除的元素用下一个不被删除的元素进行覆盖,同时返回一个迭代器,在该迭代器之前的所有元素,保留原容器的顺序,并且不存在被删除的元素,也就是你想要的容器内容,而该迭代器到容器的末尾则不变,也就是说,原容器的大小没有发生变化,被删除的元素也确实没有了,但容器末尾的一些元素(个数等于被删除元素个数)又会多出来一份,这是我们不愿意看到的,因此需要借助上面提到的erase函数。所以说remove需要和erase搭配使用才能实现完整的删除功能。

  • 单独使用remove
#include <iostream>
#include <vector>
#include <algorithm>			// [注意] :remove位于algorithm函数库中
int main()
{
    std::vector<int> vecInt{0, 1 , 2 ,3 ,4};

    std::cout << vecInt.size() << std::endl;     // 输出的结果为5,容器中存了5个元素
    std::cout << vecInt.capacity() << std::endl; // 输出的结果为5,容器在内存开辟空间的容量

    std::remove(vecInt.begin(), vecInt.end(),3);
    
    std::cout << vecInt.size() << std::endl;     // 输出的结果为5,容器中存了5个元素
    std::cout << vecInt.capacity() << std::endl; // 输出的结果为5,容器在内存开辟空间的容量

    for(auto i : vecInt)
    {
        std::cout << i << std::endl;
    }
}
// 使用remove之前,容器vector的值为: 0,1,2,3,4
// 使用remove函数删除值为3的元素后,容器vector的值为:0,1,2,4,4
// 可以看出remove,没有改变容器的size,只是将值为3的元素删除后,将后面的元素,移动到前面(后面的值还保留,因此最后是4)。
  • earse和remove配和使用
#include <iostream>
#include <vector>
#include <algorithm>		// [注意] :remove位于algorithm函数库中
int main()
{
    std::vector<int> vecInt{0, 1 , 2 ,3 ,4};

    std::cout << vecInt.size() << std::endl;     // 输出的结果为5,容器中存了5个元素
    std::cout << vecInt.capacity() << std::endl; // 输出的结果为5,容器在内存开辟空间的容量

    vecInt.erase(std::remove(vecInt.begin(), vecInt.end(), 3), vecInt.end());
    
    std::cout << vecInt.size() << std::endl;     // 输出的结果为4,容器中存了4个元素
    std::cout << vecInt.capacity() << std::endl; // 输出的结果为5,容器在内存开辟空间的容量

    for(auto i : vecInt)
    {
        std::cout << i << std::endl;
    }
}
// 使用remove之前,容器vector的值为: 0,1,2,3,4
// 使用remove函数删除值为3的元素后,容器vector的值为:0,1,2,4,
// 可以看出remove,容器的size变成了size-1,删除了值为3的元素。容器的capacity不变

注意:

  • remove位于algorithm函数库中,使用时需要调用algorithm头文件。
  • earse和remove不改变vector的容量capacity,(不会收回容器在内存中开辟的空间)

2. 使用swap删除容器的所有元素,并收回内存

  • 使用clear清空容器中的元素
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> vecInt{0, 1 , 2 ,3 ,4};

    std::cout << vecInt.size() << std::endl;     //  输出的结果为5,容器中存了5个元素
    std::cout << vecInt.capacity() << std::endl; // 输出的结果为5,容器在内存开辟空间的容量

    vecInt.clear();					// 使用clear清空容器
    
    std::cout << vecInt.size() << std::endl;     // 输出的结果为0,容器中存了0个元素
    std::cout << vecInt.capacity() << std::endl; // 输出的结果为5,容器在内存开辟空间的容量
}

可以看出使用clean可以清空vector,但是不会改变capacity,(不会收回容器在内存中开辟的空间)。

  • 使用swap清空容器中的元素
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> vecInt{0, 1, 2, 3, 4};

    std::cout << vecInt.size() << std::endl;      //  输出的结果为5,容器中存了5个元素
    std::cout << vecInt.capacity() << std::endl;  //  输出的结果为5,容器在内存开辟空间的容量

    std::vector<int>().swap(vecInt);		// 使用swap函数,将容器与空的容器交换,从而删除容器数据,并且收回内存
    
    std::cout << vecInt.size() << std::endl;     // 输出的结果为0,容器中存了0个元素
    std::cout << vecInt.capacity() << std::endl; // 输出的结果为0,容器在内存开辟空间的容量
}

使用swap函数,将容器与空的容器交换,从而删除容器所有数据,并且收回内存。

注意:

这里没有为 std::vector() 表达式传递任何参数。这意味着,此表达式将调用 vector 模板类的默认构造函数,而不再是复制构造函数。也就是说,此格式会先生成一个空的 vector 容器,再借助 swap() 方法将空容器交换给 vecInt,从而达到清空 vecInt 的目的。

3. 使用shrink_to_fit()配合其他函数,删除元素并回收内存。

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> vecInt{0, 1, 2, 3, 4};

    vecInt.pop_back(); 				// 删除容器中的最后一个元素
    std::cout << vecInt.size() << std::endl;     // 容器vecInt中元素的数量为4
    std::cout << vecInt.capacity() << std::endl; // 容器vecInt容量为5。
    
    vecInt.shrink_to_fit();				//  将vector 容器的容量缩减至和实际存储元素的个数相等
    
    std::cout << vecInt.size() << std::endl;     // 容器vecInt中元素的数量为4
    std::cout << vecInt.capacity() << std::endl; // 容器vecInt容量为4。
    
    vecInt.clear();				//清空容器

    std::cout << vecInt.size() << std::endl;     // 容器vecInt中元素的数量为0
    std::cout << vecInt.capacity() << std::endl; // 容器vecInt容量为4。

    vecInt.shrink_to_fit();			//  将vector 容器的容量缩减至和实际存储元素的个数相等

    std::cout << vecInt.size() << std::endl;     // 容器vecInt中元素的数量为0
    std::cout << vecInt.capacity() << std::endl; // 容器vecInt容量为0。
}

本文参考:

http://c.biancheng.net/view/6846.html

https://blog.youkuaiyun.com/ydyang1126/article/details/60762619

`std::vector` 是 C++ 标准库中用于存储动态数组的一种容器,而 `std::shared_ptr` 则是一个智能指针,它允许你管理内存,并自动处理资源的生命周期,避免了内存泄露的问题。 当你将 `std::shared_ptr` 放入 `std::vector` 中时,可能会遇到一些异常情况: ### 1. 内存泄漏风险 如果在向 `std::vector` 添加元素的过程中,由于某种原因导致 `std::shared_ptr` 的构造函数抛出异常,则该元素不会被添加到 `std::vector` 中,但是分配给这个 `std::shared_ptr` 的内存仍然会被保留。这可能导致内存泄漏,因为你实际上无法访问或利用这部分内存。 ```cpp #include <iostream> #include <vector> #include <memory> int main() { try { std::vector<std::shared_ptr<int>> vec; std::shared_ptr<int> ptr(new int(42)); // 这里假设有一个函数 create_shared_ptr() 可能会抛出异常 std::shared_ptr<int> ptr_err = create_shared_ptr(); vec.push_back(ptr); vec.push_back(ptr_err); // 抛出异常后,ptr_err 未加入到 vector 中 for (const auto& p : vec) { std::cout << *p << std::endl; } } catch(...) { std::cerr << "Caught an exception!" << std::endl; } return 0; } ``` ### 2. 异常传播 当在循环中或在其他需要遍历 `std::vector` 的操作中发生异常时,程序会立即停止运行,即使有部分数据已经成功插入。这种情况下,未处理的部分 `std::shared_ptr` 也有可能导致后续操作出现问题,尤其是涉及所有权转移的操作(如 `std::make_shared()` 或者通过 `std::weak_ptr` 访问时)。 ### 解决方案 为了避免上述问题,可以采用几种策略: 1. **使用 `std::optional`**:`std::optional` 提供了一种更安全的方式来存放可能存在的值,它可以捕获并忽略掉异常。 ```cpp #include <optional> // 使用 std::optional 替代 std::shared_ptr,在向 std::vector 插入前检查是否有效 ``` 2. **使用 `try-catch` 来捕捉异常**:在向 `std::vector` 添加元素之前尝试捕获可能出现的异常,并适当地处理它们,例如记录错误信息或跳过异常。 ```cpp try { vec.push_back(std::move(ptr)); // 将 ptr 移动至 vector 而不是拷贝 vec.push_back(std::move(ptr_err)); } catch(...) { // 处理异常,比如记录日志、跳过异常等 } ``` 3. **使用 RAII 管理资源**:确保所有资源都通过 RAII(Resource Acquisition Is Initialization)原则进行管理,即资源在离开作用域时自动释放。 ```cpp if(auto ptr = create_shared_ptr()) { vec.push_back(std::move(ptr)); } ``` ### 相关问题: 1. 如何在使用 `std::vector` 和 `std::shared_ptr` 结合时有效地管理异常? 2. 何时应该考虑使用 `std::optional` 而非 `std::shared_ptr` 存储变量? 3. 如何设计程序结构以避免因 `std::shared_ptr` 引发的内存泄漏和异常传播?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值