这可能是一个非常愚蠢/基本的问题,但我无法弄明白。我会告诉你我的想法 - 如果我错了,请纠正我。
当我们使用STL容器存储原始指针时,我们必须在它们之后进行清理:
std::list<animal*> alist;
animal *a = new animal();
alist.push_back(a);
...
animal *b = alist.front();
alist.pop_front();
//do stuff with b
delete b;
如果我们存储对象会怎样?我的理解是,如果一个装满了对象的容器超出范围,它内部的所有对象都将被销毁。正确的吗?
但是如果我们使用std::list<T>.pop_front()
来删除容器中的对象呢?
std::list<animal> alist;
{
animal ani();
//ani is inserted at the end of the list (it IS ani rather than a copy
//since push_back accepts const T&)
alist.push_back(ani);
} //nothing is deallocated/destroyed here
...
{
animal b = alist.front(); //b is now a copy(?!) of the front element
//created via copy constructor
alist.pop_front(); //the front element of the list is
//destroyed/destructor is called
//do stuff with b
} //b goes out of scope
答案 0 :(得分:6)
当您将某些东西存放在容器中时,您传递的任何内容都会被复制到容器 1 中。你仍然拥有原始对象 2 ;容器拥有其对象的副本。确实,push_back
(例如)通过引用获取其参数。这意味着当您将对象传递到容器中时,不需要复制用作参数本身,但放入容器的内容仍然是您要求它推送的内容的副本。通过引用传递意味着只需要一个副本;如果它是按值传递的(至少在C ++ 98/03中)会导致两个副本:从对象到函数参数的一个副本,然后是函数参数的另一个副本进入容器。通过引用传递允许直接从原始对象的单个副本直接进入容器。
同样,当您从容器中获取对象时,您将获得容器中所包含内容的副本。容器仍然有它的对象,你有你的对象。每个都有自己的生命。
当您从容器中删除或弹出一个对象时,该对象将从容器中删除并销毁 - 如果它是具有析构函数的东西,则将调用析构函数来销毁它。
当容器本身被销毁时,容器中的对象将被销毁。无论何时碰巧,你的对象都会被销毁 - 它来自容器的事实对它没有任何影响。如果它是一个局部变量,它将在超出范围时被销毁。如果它是全局的,它将持续到结束时间(对于程序)。如果从函数返回它并且用于初始化引用,则会观察到延长其生命周期的常规规则。底线:在那一点上,它只是另一个对象,它的生命周期就像以同样方式定义的任何其他对象一样。
如果您将指针(原始或智能)存储在容器中,这可能会有点...模糊。以上所有内容实际上都是正确的,但正在复制/存储的对象是一个指针。除非你使用一些“知道”你正在存储指针的容器(例如,Boost ptr_vector),它只是处理一个指针,并“忽略”某个地方有一个指针指向的对象。
emplace_back
在容器中构建一个对象,但同样的基本思想适用 - 容器有一个它拥有的对象,而你永远不要直接接触。push_back(10)
)。答案 1 :(得分:2)
是。如果存储对象而不是指向动态内存的指针,则不需要进行任何解除分配 弹出元素时。标准库负责调用弹出对象的析构函数。
规则是:
如果您分配了某些内容(称为delete
),您只需要解除分配(调用new
)。
答案 2 :(得分:2)
据我了解,如果一个装满了对象的容器超出范围,那么包含在其中的所有对象也是如此。正确的吗?
首先,一些重要的迂腐。 对象不会超出范围,标识符可以。这是一个重要的区别,可能有助于澄清这里发生了什么:
当对应于容器的标识符(变量)超出范围时,将自动删除相应的对象(容器本身)。然后它的析构函数依次删除它的每个元素。
但是如果我们使用
std::list<T>.pop_front()
来删除容器中的对象呢?
容器只删除包含的元素(就你而言,它调用它的析构函数)。 (也可能有其他的内务处理,比如在std::vector
中将所有剩余的元素拖曳到一起,这会导致大量的析构函数/复制构造函数调用。)
答案 3 :(得分:2)
如果从STL容器中删除对象,则如果它是struct / class-type,则调用其析构函数。同样取决于容器,用于存储对象的内部数据结构也被解除分配/销毁。
请记住,指针类型不是struct / class-types,因此没有可以管理指向的内存的析构函数。因此,如果要在从STL容器中删除指针时避免意外的内存泄漏,最好使用智能指针类型(如std::shared_ptr<T>
)来正确管理分配给指针的内存,并在存在时释放它。不再引用已分配的内存对象。
答案 4 :(得分:2)
std::list<animal> alist;
// A list is created in automatic storage (the stack). Its elements
// will exist in the free store (the heap)
{
animal ani;
// ani is created in automatic storage (the stack)
alist.push_back(ani);
// A copy of ani (using animal(const animal&) ctor) is created in
// the list "alist" via memory allocated on the free store (the heap)
} // ani.~animal() is called, then the storage for ani is recycled
// (ie, the stack space that ani used can be reused)
{
animal b = alist.front();
// either animal(animal&) or animal(animal const&)
// is called on the previous line, constructing an instance
// of animal called "b" in automatic storage (the stack)
// This is a copy of a copy of the animal instance called "ani"
// above, assuming nothing else besides this code has manipulated
// alist
alist.pop_front();
// The animal in the alist that is a copy of ani is destroyed
// from the free store (the heap)
//do stuff with b
} // b.~animal() is called, then the memory in automatic storage
// (the stack) that b lived in is recycled for other purposes