C++ 释放存储在向量中的对象?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3808480/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:48:14  来源:igfitidea点击:

Deallocating objects stored in a vector?

c++memory-managementvector

提问by Ian Burris

I have a class that creates a vector of objects. In the deconstructor for this class I'm trying to deallocate the memory assigned to the objects. I'm trying to do this by just looping through the vector. So, if the vector is called maps I'm doing:

我有一个创建对象向量的类。在这个类的解构器中,我试图释放分配给对象的内存。我试图通过循环遍历向量来做到这一点。所以,如果向量被称为我正在做的地图:

Building::~Building() {
    int i;
    for (i=0; i<maps.size(); i++) {
        delete[] &maps[i];
    }
}

When I run this the program segfaults while deallocating memory. I think what I'm doing is actually deleting the array storing the objects and not the objects themselves. Is this correct? If not any ideas as to what I'm doing wrong?

当我运行它时,程序在释放内存时出现段错误。我认为我正在做的实际上是删除存储对象的数组而不是对象本身。这样对吗?如果没有关于我做错了什么的任何想法?

回答by Alex Reece

It depends on how vector is defined.

这取决于矢量是如何定义的。

If maps is a vector<myClass*>you delete each element with something similar to:

如果 maps 是 avector<myClass*>你删除每个元素,类似于:

for ( i = 0; i < maps.size(); i++)
{
    delete maps[i];
}

If maps is a vector<myClass>I don't think you need to delete the individual elements.

如果 maps 是 avector<myClass>我认为您不需要删除单个元素。

回答by Eric Rahm

It's hard to tell from the terminology you've used and the code presented exactly what's going on. So maybe a few examples would help you out.

很难从您使用的术语和代码中准确地说明发生了什么。所以也许一些例子会帮助你。

Array new and array delete

数组新建和数组删除

What's up with new []and delete []you ask? These guys are used for allocating/deallocating arrays of things. Those things could be POD or they could be full fledged objects. For objects they will call the constructor after allocating and destructor while deallocating.

用什么的了new []delete []你问?这些家伙用于分配/解除分配事物的数组。这些东西可能是 POD,也可能是成熟的对象。对于对象,它们将在分配后调用构造函数,在释放时调用析构函数。

Let's take a contrived example:

让我们举一个人为的例子:

class MrObject
{
public:
   MrObject() : myName(new char[9]) { memcpy(myName, "MrObject", 9); }
   virtual ~MrObject() { std::cout << "Goodbye cruel world!\n"; delete [] myName; }
private:
   char* myName;
};

Now we can do some fun stuff with MrObject.

现在我们可以用 MrObject 做一些有趣的事情。

Arrays of objects

对象数组

First let's create a nice and simple array:

首先让我们创建一个漂亮而简单的数组:

MrObject* an_array = new MrObject[5];

This gives us an array of 5 MrObjects, all nicely initialized. If we want to delete that array we should perform an array delete, which in turn will call the destructor for each MrObject. Let's try that:

这为我们提供了一个由 5 个 MrObject 组成的数组,所有这些都很好地初始化。如果我们想删除那个数组,我们应该执行一个数组删除,它依次为每个 MrObject 调用析构函数。让我们试试:

delete [] an_array;

But what if we goofed up and just did a normal delete? Well now's a good time to try it for yourself

但是如果我们搞砸了,只是做了一个普通的删除呢?好吧,现在是自己尝试的好时机

delete an_array;

You'll see that only the first destructor get's called. That's because we didn't delete the whole array, just the first entry.

你会看到只有第一个析构函数被调用。那是因为我们没有删除整个数组,只是删除了第一个条目。

Well sometimes. It's really undefined what happens here. The takeaway is to use the array form of delete when you use an array new, ditto for just plain old new and delete.

嗯,有时。这里发生的事情真的是不确定的。要点是在使用数组 new 时使用 delete 的数组形式,对于普通的 new 和 delete 也是如此。

Vectors of Objects

对象的向量

OK, so that was fun. But let's take a look at the std::vector now. You'll find that this guy will manage the memory for you, and when he goes out of scope, well so does everything he's holding onto. Let's take him out for a test ride:

好的,那很有趣。但是现在让我们看看 std::vector 。你会发现这个人会为你管理内存,当他超出范围时,他所持有的一切也会如此。让我们带他出去试驾一下:

std::vector<MrObject> a_vector(5);

Now you have a vector with 5 initialized MrObjects. Let's see what happens when we clear that sucker out:

现在你有一个带有 5 个初始化 MrObjects 的向量。让我们看看当我们清除那个吸盘时会发生什么:

a_vector.clear();

You'll note that all 5 destructors got hit.

您会注意到所有 5 个析构函数都被击中。

Vectors of Pointers to Objects

指向对象的指针向量

Oooooh you say, but now lets get fancy. I want all the goodness of the std::vector, but also want to manage all the memory myself! Well there's a line for that as well:

哦哦,你说,但现在让我们想象一下。我想要 std::vector 的所有优点,但也想要自己管理所有内存!好吧,还有一条线:

std::vector<MrObject*> a_vector_of_pointers(5);
for (size_t idx = 0; idx < 5; idx++) {
   // note: it's just a regular new here, not an arra
   a_vector_of_pointers[idx] = new MrObject;
}

See that was a bit more of a pain. But it can be useful, you could use a non-default constructor when creating MrObject. You could put derived MrObjects in there instead. Well as you can see the sky's the limit. But wait! You created that memory, you best manage it. You'll want to loop over each entry in the vector and cleanup after yourself:

看到那更痛苦了。但它可能很有用,您可以在创建 MrObject 时使用非默认构造函数。你可以把派生的 MrObjects 放在那里。那么你可以看到天空的极限。可是等等!你创造了那个记忆,你最好地管理它。您需要循环遍历向量中的每个条目并在您自己之后进行清理:

for (size_t idx = 0; idx < a_vector_of_pointers.size(); idx++) {
   delete a_vector_of_pointers[idx];
}

回答by San Jacinto

In C++, you can only delete data by pointer. You've accomplished this using the & operator, but if your vector doesn't contain pointers that point to memory allocated on the machines heap (not the stack, as is the method when you have a normal variable declaration) then you can TRY to delete it, but you will encounter undefined behavior (which will hopefully cause a program crash).

在 C++ 中,只能通过指针删除数据。您已经使用 & 运算符完成了此操作,但是如果您的向量不包含指向在机器堆上分配的内存的指针(不是堆栈,就像您拥有普通变量声明时的方法一样),那么您可以尝试删除它,但您会遇到未定义的行为(这可能会导致程序崩溃)。

When you insert into a vector, the vector calls the class's copy constructor and you're actually inserting a copy of the object. If you have a function whose sole purpose is like the following:

当您插入向量时,向量会调用类的复制构造函数,而您实际上是在插入对象的副本。如果您有一个函数,其唯一目的如下:

void insertObj(obj & myObject)
{
  myVector.insert(myObject);
}

Then realize that there are twoobj's in this scope: the one you passed in by reference, and the copy in the vector. If instead we had pass in myObject by value and not by reference, then we could say that two copies of the object exist in this scope, and one exists in the caller. In each of these 3 instances, they are notthe same object.

然后意识到在这个范围中有两个obj :你通过引用传入的一个,以及向量中的副本。相反,如果我们通过值而不是通过引用传入 myObject,那么我们可以说该对象的两个副本存在于该作用域中,一个存在于调用者中。在这 3 个实例中的每一个中,它们都不是同一个对象。

If you are instead storing pointers in the container, then the vector will create a copy of the pointer (NOTa copy of the object) and will insert the copied pointer into the vector. It is usually not a good practice to insert elements into a container by pointer unless you knowthat the object will live at least until the container is done with it. For example,

如果您将指针存储在容器中,则向量将创建指针的副本(不是对象的副本)并将复制的指针插入向量中。通过指针将元素插入到容器中通常不是一个好习惯,除非您知道该对象至少会在容器使用完毕之前一直存在。例如,

void insert()
{
  Obj myObj;
  myVector.insert(&myObj);
}

Is probably a very bad idea, as you'd have a pointer in the vector that points to an object that is destroyed automatically when it goes out of scope!

这可能是一个非常糟糕的主意,因为您在向量中有一个指针指向一个对象,当它超出范围时会自动销毁!

Point being, if you malloc'd or new'd your object, then you need to free or delete it. If you created it on the stack, then do nothing. The vector will take care of it when it is destroyed.

重点是,如果你 malloc 或 new 了你的对象,那么你需要释放或删除它。如果你在堆栈上创建它,那么什么都不做。当它被销毁时,vector 会处理它。

For a deeper understanding of stack-based allocation vs. heap-based allocation, see my answer here: How does automatic memory allocation actually work in C++?

要更深入地了解基于堆栈的分配与基于堆的分配,请在此处查看我的回答: 自动内存分配在 C++ 中实际上如何工作?

回答by Roman Solodyashkin

for(std::vector<MyObjectClass>::iterator beg = myVector->begin(), end = myVector->end(); beg != end; beg++)
{
    delete *beg;
}
myVector->clear();

回答by Guy Avraham

I decided to turn my comment into an answer (along with the other great answers here), so here it goes.

我决定把我的评论变成一个答案(连同这里的其他很棒的答案),所以就这样了。

I would note again, that this case deals with inheritance of the object.

我要再次指出,这种情况涉及对象的继承。

When you delete an array of Derived object, pointed by a Base pointer, as follows:

当你删除一个由Base指针指向的Derived对象数组时,如下:

Base* pArr = new Derived[3];

delete [] pArr;

What the compiler does "under the hood" is to generate the following code:

编译器在“幕后”所做的是生成以下代码:

//destruct the objects in *pArr in the inverse order
//in which they were constructed
for (int i = the number of elements in the array - 1; i >= 0; --i)
{
     pArr[i].Base::~Base(); 
}

Now, when doing so, we get undefined behavior. Dealing with arrays is simply dealing with offsets so when this loop occurs, in each iteration of the loop the pointer of the array is incremented according to the size of Base --> and here is where things becomes "undefined". In the "simple" (yet less common) case where the Derived class does not add any members of its own, its size is as the size of Base --> so things might (I guess that not always) work well. But (!!) when you add at least one member to the Derived class, its size grows, causing the offset increment in each iteration to be wrong.

现在,这样做时,我们会得到未定义的行为。处理数组只是处理偏移量,所以当这个循环发生时,在循环的每次迭代中,数组的指针会根据 Base 的大小增加 --> 在这里,事情变得“未定义”。在 Derived 类不添加任何自己的成员的“简单”(但不太常见)的情况下,它的大小与 Base 的大小一样 --> 所以事情可能(我猜这并不总是)运作良好。但是 (!!) 当你向 Derived 类添加至少一个成员时,它的大小会增长,导致每次迭代中的偏移增量是错误的。

To illustrate this case I have create the following Base and Derived objects. Note that in the case that Derived does not containthe m_cmember, the delete operation goes well (comment it out and see for yourself), YET once you add it, I got a segmentation fault (which is the undefined behavior).

为了说明这种情况,我创建了以下 Base 和 Derived 对象。注意,在派生的情况下不包含M_C成员,删除操作顺利(注释一下,看看自己),但一旦你添加它,我得到一个分段错误(这是不确定的行为)。

#include <iostream>
using namespace std;

class Base 
{

    public:
        Base(int a, int b)
        : m_a(a)
        , m_b(b)    
        {
           cout << "Base::Base - setting m_a:" << m_a << " m_b:" << m_b << endl;
        }

        virtual ~Base()
        {
            cout << "Base::~Base" << endl;
        }

        protected:
            int m_a;
            int m_b;
};


class Derived : public Base
{
    public:
    Derived() 
    : Base(1, 2) , m_c(3)   
    {

    }

    virtual ~Derived()
    {
        cout << "Derived::Derived" << endl;
    }

    private:    
    int m_c;
};

int main(int argc, char** argv)
{
    // create an array of Derived object and point them with a Base pointer
    Base* pArr = new Derived [3];

    // now go ahead and delete the array using the "usual" delete notation for an array
    delete [] pArr;

    return 0;
}

回答by SingleNegationElimination

It's hard to say from your question just what the signature of mapsis. I'm guessing you want to use delete[]because you also used new[]. So does that mean the members of your vector is itself a collection? supposing it is, then you have something like this:

从你的问题很难说签名maps是什么。我猜您想使用,delete[]因为您还使用了new[]. 那么这是否意味着您的向量的成员本身就是一个集合?假设是这样,那么你有这样的事情:

class Building {
  public:
    typedef int* maps_t;
  private:
    std::vector<maps_t> maps;
  public:
    Building();
    ~Building();
};

Building::Building(size_t num_maps) {
  for(;num_maps; --num_maps)
  {
    maps.push_back(new Building::maps_t[10]);  
  }
}

in that case, your destructor is nearly right; you need change only &maps[i]to maps[i].

在这种情况下,您的析构函数几乎是正确的;您只需要更改&maps[i]maps[i].

Building::~Building() {
    int i;
    for (i=0; i<maps.size(); i++) {
        delete[] maps[i];
    }
}

But in C++, we rarely like to do things that way. For one thing, unless you are actually trying to implement something like std::vector, you rarely want to use new[]or delete[]explicitly. You can, for example, use std::vector. You need perform no explicit memory management in that case. Your class will look like so:

但是在 C++ 中,我们很少喜欢这样做。一方面,除非您实际上是在尝试实现类似的东西,否则您std::vector很少想使用new[]delete[]显式。例如,您可以使用std::vector. 在这种情况下,您不需要执行显式内存管理。你的类看起来像这样:

class Building {
  public:
    typedef std::vector<int> maps_t;
  private:
    std::vector<maps_t> maps;
  public:
    Building();
};

Building::Building(size_t num_maps) {
  for(;num_maps; --num_maps)
  {
    maps.push_back(Building::maps_t(10));  
  }
}

There is no user defined destructor in this case, because std::vectoralready manages its own memory quite well.

在这种情况下没有用户定义的析构函数,因为它std::vector已经很好地管理了自己的内存。

回答by John Carter

If you're using std::vectorthen you can just let it get handled by the destructor for vector, assuming there are "objects" (and not pointers to objects) in said vector.

如果您正在使用,std::vector那么您可以让它由 for 的析构函数处理vector,假设在 say 中有“对象”(而不是指向对象的指针)vector

-- or --

- 或者 -

If you're using a standard array as the "vector":

如果您使用标准数组作为“ vector”:

The purpose of the "delete []" variation is to deallocate an entire array, and hence avoid the need to have a forloop like you do.

" delete []" 变体的目的是释放整个数组,从而避免for像您一样需要循环。

If using standard C/C++ arrays, "delete [] maps" should do it for you. "[]" shouldn't be used for deallocating STL vectors.

如果使用标准 C/C++ 数组," delete [] maps" 应该为你做。" []" 不应该用于释放 STL vectors。