释放STL向量中的对象

Freeing an object in an STL vector

本文关键字:对象 向量 STL 释放      更新时间:2023-10-16

我有下面的例子来释放STL向量中的对象。

#include <map>
#include <string>

using namespace std;
class Test
{
   public:
      char*  name;
      int id;
      Test(char* n, int i);
};
Test::Test(char* n, int i)
{
   name = n;
   id = i;
}

int main ()
{
        Test* t = new Test("hi", 5);
        vector<Test> v;
        v.insert(v.end(), *t);
        for(vector<Test>::iterator it = v.begin(); it != v.end(); it++)
        {
                if (it->id == 5)
                {
                        Test* ptr = &*it;
                        v.erase(it);
                        delete ptr;
                        break;
                }
        }
        return 0;
}

根据我一直在研究的情况,这应该是正确的方法。然而,valgrind给了我这样的抱怨:

==7404== Invalid free() / delete / delete[]
==7404==    at 0x4A05130: operator delete(void*) (vg_replace_malloc.c:244)
==7404==    by 0x400FD2: __gnu_cxx::new_allocator<Test>::deallocate(Test*, unsigned long) (new_allocator.h:94)
==7404==    by 0x401004: std::_Vector_base<Test, std::allocator<Test> >::_M_deallocate(Test*, unsigned long) (stl_vector.h:133)
==7404==    by 0x401045: std::_Vector_base<Test, std::allocator<Test> >::~_Vector_base() (stl_vector.h:119)
==7404==    by 0x40109C: std::vector<Test, std::allocator<Test> >::~vector() (stl_vector.h:272)
==7404==    by 0x400998: main (test.cc:46)
==7404==  Address 0x4C58070 is 0 bytes inside a block of size 16 free'd
==7404==    at 0x4A05130: operator delete(void*) (vg_replace_malloc.c:244)
==7404==    by 0x40098A: main (test.cc:41)

还有内存泄漏。做这件事的正确方法是什么?

这是的老规则

每一个new都应有一个delete

在您的情况下,调用new的唯一时间是分配Test的实例(目前还不清楚为什么要这样做,但我们忽略它)。然后将该对象的副本插入vector;您没有为插入的对象分配内存。因此,不需要在vector管理的内存上调用delete

另一方面,您正在泄漏内存,因为您从未对分配的内存调用delete。添加

delete t;

在您使用完t 后的某个地方的代码中


现在,如果您的向量被声明为vector<Test *>,那么在从向量中删除元素之前,您需要手动delete元素。但是,如果您需要一个指针向量,那么您应该始终使用vector<unique_ptr<Test>>(或其他智能指针,或boost::ptr_vector),在这种情况下,不需要手动调用delete

测试*t=新测试("hi",5);

您正在泄漏此对象。您将对象的副本存储到vector中,但之后不会释放原始对象。

v.insert(v.end(),*t);

请改用v.push_back(*t)

if(it->id==5);

分号是错误的。你需要删除它。

测试*ptr=&它v.擦除(它);删除ptr;

erase()可以,但delete不行。您没有在vector中存储用new分配的对象,因此根本不应该尝试delete。你试图释放你不拥有的记忆。

话虽如此,你有两个选择:

  1. vector:中存储指针

    int main ()
    {
        Test* t = new Test("hi", 5);
        vector<Test*> v;
        v.push_back(t);
        for(vector<Test*>::iterator it = v.begin(); it != v.end(); ++it)
        {
            Test* ptr = *it;
            if (ptr->id == 5)
            {
                v.erase(it);
                delete ptr;
                break;
            }
        }
        return 0;
    }
    
  2. 不要在矢量中存储指针:

    int main ()
    {
        /*
        Test *t = new Test("hi", 5);
        vector<Test> v;
        v.push_back(*t);
        delete t; // don't forget this!
        */
        vector<Test> v;
        v.push_back(Test("hi", 5));
        for(vector<Test>::iterator it = v.begin(); it != v.end(); ++it)
        {
            if (it->id == 5)
            {
                v.erase(it);
                break;
            }
        }
        return 0;
    }
    

仅供参考,您可以考虑使用std::find_if()而不是手动循环:

struct IsId
{
    int _id;
    IsId(int id) : _id(id) {}
    bool operator()(const Test &src) const { return (src.id == _id); }
};

vector<Test>::iterator it = find_if(v.begin(), v.end(), IsId(5));
if (it != v.end())
{
    v.erase(it);
}

vector::erase:

从矢量中删除单个元素(位置)或一系列元素([第一个,最后一个))。

这有效地减少了容器大小,减少了被销毁的元素数量。

所以你只需要使用擦除功能。你不需要使用指针和删除。

请注意,当您将新实例插入vector:时

v.insert(v.end(), *t);

该类在向量中再次被复制构造,因此您只剩下两个单独的实例,一个在t中,一个位于v[0]中。

用sane C++编写代码的正确方法如下:

int main()
{
    std::vector<Test> v { Test(""hi", 5) };
}   // returns 0, frees all allocated resources