如何正确分配和解除分配向量图

Posted

技术标签:

【中文标题】如何正确分配和解除分配向量图【英文标题】:How to properly allocate and deallocate a map of vectors 【发布时间】:2020-03-20 15:38:43 【问题描述】:

我试图将数据保存在无序的向量映射中,使用 uint32_t 地址作为键。向量需要存储在堆中,所以 即使函数超出范围,我仍然可以访问数据。我是这样分配的:

using vectorBuffer = std::vector<uint8_t>;
using I2cVectorMap = std::unordered_map<address_t, vectorBuffer>;
using I2cVectorEntry = std::pair<address_t, vectorBuffer>;

if(auto i2cIter = i2cVectorMap.find(address); i2cIter == i2cVectorMap.end()) 
    vectorBuffer * receiveVector = new vectorBuffer(maxReplyLen);
    i2cVectorMap.insert(I2cVectorEntry(address, *receiveVector));

else 
    // Already in map. readjust size
    vectorBuffer * existingVector = &i2cIter->second;
    existingVector->resize(maxReplyLen);
    existingVector->shrink_to_fit();

像这样的释放(根据各种来源的建议,将向量与空向量交换):

// getting the address from another object.
address_t deviceAddress = i2cCookie->getAddress();
if(auto i2cIter = i2cVectorMap.find(deviceAddress); i2cIter != i2cVectorMap.end()) 
    vectorBuffer().swap(i2cIter->second);

这是一个正确的实现吗?我会将地址传递给映射条目,或者传递给启动 I2C 传输的驱动程序函数,或者传递给另一个处理数据的对象。我想确保我没有内存泄漏。

提前非常感谢!

【问题讨论】:

您的地图包含完整的矢量 - 因此创建新地图条目会将该矢量复制到地图中。如所写,您的代码存在内存泄漏。您在堆上动态创建vectorBuffer,将其内容复制到映射中,然后丢失指向堆上内存的指针。你可能想在地图上emplace 你的vectorBuffer。那么你根本不需要担心内存管理。 en.cppreference.com/w/cpp/container/unordered_map/emplace 一般要点:您几乎从不想动态分配 C++ 标准库容器。他们用他们最好的魔法来管理自己。旁注:vectorBuffer * existingVector = &amp;i2cIter-&gt;second; 可能是 vectorBuffer &amp; existingVector = i2cIter-&gt;second; 这并不是那么重要,但如果您需要 answer = existingVector[42];,则指针会使使用 [] 运算符非常困难。 【参考方案1】:

不,您不应该使用new。首先需要直接news 的情况很少见。

您无需手动创建任何具有动态存储持续时间(“在堆上”)的对象。标准库容器直接存储对象,而不是对象的引用或指针。他们将为这些对象分配并自己构造它们。您只需向容器成员函数提供构造函数参数,容器(复制/移动)从中构造存储的元素。

您可以将对象声明为自动变量(即“在堆栈上”):

vectorBuffer receiveVector(maxReplyLen);
i2cVectorMap.insert(I2cVectorEntry(address, receiveVector));

或为中间向量实例使用临时变量而不是命名变量(这也将使用移动而不是复制操作,因此性能更好):

i2cVectorMap.insert(I2cVectorEntry(address, vectorBuffer(maxReplyLen)));

如果您使用emplace 而不是insert,您也可以删除冗余对类型:

i2cVectorMap.emplace(address, vectorBuffer(maxReplyLen));

您不需要指针来调用映射中对象的方法:

vectorBuffer& existingVector = i2cIter->second;
existingVector.resize(maxReplyLen);
existingVector.shrink_to_fit();

要从地图中删除一个元素,您可以使用它的.erase 成员函数:

i2cVectorMap.erase(deviceAddress);

你不需要做任何其他事情。这也会破坏和释放向量。

当您不再需要地图及其包含的矢量时,您也无需执行任何操作。地图的析构函数会在地图定义的范围离开时自动清理所有内容。

【讨论】:

非常感谢!我合并了这些更改。我从未见过左侧的地址运算符。这种方法有一些优点吗? (我猜这部分由 user4581301 回答) @Spacefish 这是定义reference的语法。这是 C++ 中非常基础的语言结构。如果您以前没有听说过,您需要查看good introductory book。 @Spacefish 如果您已经熟悉 C 和其他 C 派生语言之类的语言,请here's a very terse "book" 帮助您入门。如果它向您提供的信息的深度和速度吓到您购买更多书籍,我认为这是一件好事。 C++ 非常复杂,仅仅弄清楚你想要使用哪些特性以及你不想使用哪些特性就需要大量阅读。 我对 C++ 还是比较陌生,尤其是对现代 C++ 语言的特性,非常感谢您的建议。

以上是关于如何正确分配和解除分配向量图的主要内容,如果未能解决你的问题,请参考以下文章

与解除分配堆数组相关的语法混淆[重复]

C:指向结构指针数组的指针(分配/解除分配问题)

C 和 C++:释放已分配指针的一部分

iOS 设备上 ObjC 对象的安全解除分配

如何解除分配 CCLayer

删除、释放还是解除分配?