首页 > 其他分享 > 18.vector越界访问下标,map越界访问下标?vector删除元素时会不会释放空间?

18.vector越界访问下标,map越界访问下标?vector删除元素时会不会释放空间?

时间:2023-08-02 22:57:01浏览次数:36  
标签:std 下标 元素 越界 vector Vec swap vec

18.vector越界访问下标,map越界访问下标?vector删除元素时会不会释放空间?

1.vector越界访问下标

std::vector是C++标准库中的一种动态数组,其大小可以根据需要进行调整。当你试图访问一个不存在的元素,即访问超出其当前大小范围的索引时,将会发生越界访问。

在C++中,如果你使用operator[]来访问std::vector的元素,当下标越界时,编译器不会抛出任何错误或异常,而且它通常会返回一个未定义的值,这可能导致程序行为异常或崩溃。这种情况下,错误可能很难被检测到,因为程序可能会在某些情况下正常运行,但在其他情况下出现错误。

为了避免这种情况,C++提供了std::vector::at()成员函数,这个函数在访问超出std::vector范围的索引时会抛出std::out_of_range异常。这可以帮助你立即发现并处理错误。

下面是一个例子:

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v = { 1, 2, 3, 4, 5 };

    try 
    {
        // 正常访问
        std::cout << v.at(2) << std::endl;  // 输出:3

        // 越界访问
        std::cout << v.at(10) << std::endl;
    }
    catch (const std::out_of_range& e)
    {
        std::cerr << "Caught an out_of_range exception: " << e.what() << std::endl;
    }

    return 0;
}

输出:

3
Caught an out_of_range exception: invalid vector subscript

在这个例子中,当我们试图通过v.at(10)访问不存在的元素时,程序会抛出std::out_of_range异常,并输出异常信息。

总的来说,为了避免越界访问,你应该始终确保你访问的索引在0到vector.size() - 1的范围内。在需要的情况下,使用std::vector::at()而不是operator[]可以提供额外的越界检查。

2.map越界访问下标

在C++的std::map中,使用operator[]访问一个不存在的键会创建一个具有该键和默认值(通常为0或等效初始化值)的新元素。

这是一个例子:

#include <iostream>
#include <map>

int main() 
{
    std::map<std::string, int> m;
    m["Alice"] = 20;

    std::cout << m["Alice"] << std::endl;  // 输出:20

    // 访问一个不存在的键
    std::cout << m["Bob"] << std::endl;  // 输出:0

    return 0;
}

输出:

20
0

在这个例子中,尝试访问m["Bob"]会在std::map中创建一个新的键值对"Bob": 0

如果你只想查找std::map中的元素,而不想在找不到元素时创建新元素,你可以使用std::map::find()函数。这个函数在找到键时返回一个指向该键值对的迭代器,否则返回std::map::end()

auto it = m.find("Bob");
if (it != m.end()) 
{
    std::cout << it->second << std::endl;
}
else
{
    std::cout << "Key not found" << std::endl;
}

总的来说,对于std::map的访问,你需要注意operator[]的这种特殊行为。如果你不希望在键不存在时创建新元素,你应该使用std::map::find()

3.vector删除元素时会不会释放空间?

在C++的std::vector中,当你使用erase函数删除元素时,被删除元素占用的空间会被立即释放,而且其他元素可能会进行移动以填补空出来的空间。这是因为std::vector要保证元素在内存中的连续存储。

然而,虽然erase函数会删除元素并减小std::vector的大小(可以通过size函数获取),但它不会减小std::vector的容量(可以通过capacity函数获取)。容量是std::vector预分配的内存空间,它可能大于或等于std::vector的当前大小。

如果你删除了大量元素并希望减小std::vector的容量以节省内存,你可以使用shrink_to_fit函数,或者使用下面的"swap trick"技巧:

std::vector<int>(v).swap(v);

这个技巧创建了一个新的临时std::vector(其大小等于原std::vector的大小,但容量等于或接近其大小),然后与原std::vector进行交换,从而使原std::vector的容量减小。

但是请注意,缩小std::vector的容量可能需要额外的内存分配和元素复制,所以如果你知道你将再次需要这些空间,那么可能不值得缩小容量。

Vector如何释放空间?

由于vector的内存占用空间只增不减,比如你首先分配了10000个字节,然后erase掉后面9999个,留下一个有效元素,但是内存占用仍为10000个。所有内存空间是在vector析构时候才能被系统回收。empty()用来检测容器是否为空的,clear()可以清空所有元素。但是即使clear(),vector所占用的内存空间依然如故,无法保证内存的回收。

如果需要空间动态缩小,可以考虑使用deque。

如果使用vector,可以用swap()来帮助你释放多余内存或者清空全部内存。

vector(Vec).swap(Vec); //将Vec中多余内存清除; 
vector().swap(Vec); //清空Vec的全部内存;

当使用vector(Vec).swap(Vec)这样的语句时,它实际上执行了以下步骤:

  1. 创建临时vector对象:vector(Vec)会创建一个临时的vector对象,其中的元素是拷贝自Vec的元素,但临时vector并不会预留多余的内存空间。也就是说,临时vector的容量(capacity)与实际存储的元素个数(size)相同,没有多余的内存空间。

  2. swap函数交换内容:Vec和临时vector的内容进行了交换,也就是它们的指针和容量等信息进行了交换。这个交换是非常高效的,因为swap只交换了指针,而没有真正移动元素。

  3. 临时vector析构:在整个语句执行完毕后,临时vector对象会被立即析构,因为它是在这个语句的作用域中创建的。在析构时,临时vector会释放它所持有的内存空间。由于临时vector的容量和实际存储的元素个数相同,所以这里释放的就是Vec之前的多余内存。

让我们通过一个例子来说明这个过程:

#include <vector>
#include <iostream>

int main()
{
    std::vector<int> Vec;
    for (int i = 1; i <= 10; ++i)
    {
        Vec.push_back(i); // 添加元素1-10到Vec
    }

    std::cout << "Vec Size: " << Vec.size() << ", Capacity: " << Vec.capacity() << std::endl; // 输出 Size: 10, Capacity: 16

    // 使用 vector(Vec).swap(Vec) 释放多余内存
    std::vector<int>(Vec).swap(Vec);

    std::cout << "Vec Size: " << Vec.size() << ", Capacity: " << Vec.capacity() << std::endl; // 输出 Size: 10, Capacity: 10

    return 0;
}

在这个例子中,一开始Vec的容量是16(这是一个比实际存储元素个数大的值,用于提高效率)。通过vector(Vec).swap(Vec)语句后,Vec的容量变为10,说明多余的内存被成功释放了。

vector().swap(Vec);语句中,vector()创建了一个空的临时vector对象,然后通过swap函数将Vec和这个临时vector的内容进行交换。结果Vec变为空,同时释放了所有之前的内存。

需要注意的是,这两种方法只能释放vector多余的内存,而不能释放vector中元素本身可能占用的动态分配的内存。例如,如果vector中存储的是指向动态分配对象的指针,那么这些动态分配的对象的内存需要单独释放。

实例

#include <iostream>
#include <vector>
using namespace std;

int main ()
{
    vector<int> vec (100,100);   // three ints with a value of 100
    vec.push_back(1);
    vec.push_back(2);
    cout <<"vec.size(): " << vec.size() << endl;
    cout <<"vec.capacity(): " << vec.capacity() << endl;

    vector<int>(vec).swap(vec); //清空vec中多余的空间,相当于vec.shrink_to_fit();

    cout <<"vec.size(): " << vec.size() << endl;
    cout <<"vec.capacity(): " << vec.capacity() << endl;

    vector<int>().swap(vec); //清空vec的全部空间

    cout <<"vec.size(): " << vec.size() << endl;
    cout <<"vec.capacity(): " << vec.capacity() << endl;

    return 0;
}

输出:

运行结果:
vec.size(): 102
vec.capasity(): 200
vec.size(): 102
vec.capasity(): 102
vec.size(): 0
vec.capasity(): 0

标签:std,下标,元素,越界,vector,Vec,swap,vec
From: https://www.cnblogs.com/codemagiciant/p/17602031.html

相关文章

  • 9.vector与list的区别与应用?怎么找某vector或者list的倒数第二个元素
    9.vector与list的区别与应用?怎么找某vector或者list的倒数第二个元素1.vector数据结构vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。因此能高效的进行随机存取,时间复杂度为o(1);但因为内存空间是连续的,所以在进行插入和删除操作时,会造成内存块的拷贝,时间复杂度为o(......
  • YOLOv8+DeepSORT多目标跟踪(行人车辆计数与越界识别)
    课程链接:https://edu.51cto.com/course/34407.html本课程使用YOLOv8和DeepSORT对视频中的行人、车辆做多目标跟踪计数与越界识别,开展YOLOv8目标检测和DeepSORT多目标跟踪强强联手的应用。课程分别在Windows和Ubuntu系统上做项目演示,并对DeepSORT原理和代码做详细解读(使用PyCharm单......
  • Vector Packet Processor(VPP)使用简介
    [email protected],2023DescriptionVPP的简介以及安装简介最早的VPP是由Cisco提出来的,不过现在已经开源了。FD.io的VectorPacketProcessor(VPP)是一个快速、可扩展的2-4层多平台网络协议栈,可以运行多种架构的如x86\ARM\Power架构的Li......
  • 《向量数据库指南》——2023年7月国产向量数据库排行榜Top3:Milvus,Milvus Cloud,Tencent
    向量数据库排行榜分析报告随着人工智能和大数据技术的不断发展,向量数据库在各个领域的应用越来越广泛。向量数据库是一种存储和管理大规模向量数据的数据库,具有高效的数据查询和分析能力,是人工智能领域的重要基础架构。在本文中,我们将对2023年7月的国产向量数据库排行榜进行分析和......
  • 【数据结构】vector用法
    1.初始化:vector<类型>标识符vector<类型>标识符(最大容量)vector<类型>标识符(最大容量,初始所有值)inti[5]={1,2,3,4,5}vector<类型>vi(i,i+2);//得到i索引值为3以后的值vector<vector<int>>v;二维向量//这里最外的<>要有空格。否则在比较旧的编译器下无法通过2.常......
  • unsigned char数组拷贝数据段,置0数据段,未越界方法
    需要注意的是,自己去写C语言场景下数组的拼接,得不偿失,因为涉及到了数组的拼接,就会有动态分配内存,目前C语言是没有这个功能的;自己写会容易内存报错;因此,在涉及到C语言数组时,最好是只有拷贝,赋值等操作,在一开始确定数组时,把内存分配大一些;这里主要对内存越界的地方做个封装;然后可以实......
  • 【ACM专项练习#02】整行字符串、输入vector、打印图形、处理n组数据以及链表操作等
    输入整行字符串平均绩点题目描述每门课的成绩分为A、B、C、D、F五个等级,为了计算平均绩点,规定A、B、C、D、F分别代表4分、3分、2分、1分、0分。输入有多组测试样例。每组输入数据占一行,由一个或多个大写字母组成,字母之间由空格分隔。输出每组输出结果占一行。如果输入的大......
  • 2023-07-23:给你 n 个任务和 m 个工人 每个任务需要一定的力量值才能完成 需要的力量值
    2023-07-23:给你n个任务和m个工人每个任务需要一定的力量值才能完成需要的力量值保存在下标从0开始的整数数组tasks中第i个任务需要tasks[i]的力量才能完成每个工人的力量值保存在下标从0开始的整数数组workers中第j个工人的力量值为workers[j]每个工人只......
  • C++数组下标可以是负数
     inta[5]={0,1,2,3,4}int*p=a+4;cout<<p[-2]<<endl;//2p[-2]表示从指针当前位置向前寻址两个数据类型长度注1:只有在p是指针时才能这么做,不应当出现a[-2]这样数组名加负数下标的用法,因为会超出数组地址范围注2:一般不建议这么做,可能会出现各种寻址......
  • vector的用法
    1.打印函数12template<typenameContainer>13voiddisplay(Container&con){14for(auto&elem:con){15cout<<elem<<"";16}17cout<<endl;18}......