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

发布时间 2023-08-02 22:52:52作者: CodeMagicianT

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