如果在堆中创建了像vector中的vector这样的容器对象,它们是如何被销毁的?
修改
如果容器包含指针,那么如何销毁这些指针对象
答案 0 :(得分:19)
指针的STL容器不会清理指向的数据。它只会清理持有指针的空间。如果您希望向量清理指针数据,则需要使用某种智能指针实现:
{
std::vector<SomeClass*> v1;
v1.push_back(new SomeClass());
std::vector<boost::shared_ptr<SomeClass> > v2;
boost::shared_ptr<SomeClass> obj(new SomeClass);
v2.push_back(obj);
}
当该范围结束时,两个向量将释放其内部数组。 v1将泄漏创建的SomeClass,因为只有指向它的指针才在数组中。 v2不会泄漏任何数据。
答案 1 :(得分:5)
如果你有vector<T*>
,你的代码需要在删除向量之前删除那些指针:否则,该内存被泄露。
知道C ++不做垃圾收集,这是一个原因的例子(语法错误的应用,自从我编写C ++以来已经有一段时间了):
typedef vector<T*> vt;
⋮
vt *vt1 = new vt, *vt2 = new vt;
T* t = new T;
vt1.push_back(t);
vt2.push_back(t);
⋮
delete vt1;
最后一行(delete vt1;
)显然不应该删除它包含的指针;毕竟,它也在vt2中。所以它没有。删除vt2
。
(如果你想要一个在destroy上删除指针的矢量类型,当然可以编写这样的类型。可能已经写过。但要注意删除其他人仍然持有副本的指针。)
答案 2 :(得分:2)
当向量超出范围时,编译器会对其析构函数发出一个调用,从而释放堆上已分配的内存。
答案 3 :(得分:2)
这有点用词不当。与大多数STL容器一样,向量由2个逻辑部分组成。
虽然可配置,但#2几乎总是存在于堆上。但是#1可以存在于堆栈或堆中,它只取决于它的分配方式。例如
void foo() {
vector<int> v;
v.push_back(42);
}
在这种情况下,#1部分存在于堆栈中。
现在#2如何被摧毁?当矢量的第一部分被破坏时,它也将破坏第二部分。这是通过删除vector类的析构函数中的底层数组来完成的。
答案 4 :(得分:2)
如果将指针存储在STL容器类中,则需要在对象被销毁之前手动删除它们。这可以通过循环整个容器并删除每个项目,或使用某种智能指针类来完成。但是,请不要使用auto_ptr,因为根本不能使用容器。
这样做的一个好的副作用是你可以在你的程序中保留多个指针容器,但只有那些容器拥有这些容器,你只需要清理那个容器。
删除指针的最简单方法是:
for (ContainerType::iterator it(container.begin()); it != container.end(); ++it)
{
delete (*it);
}
答案 5 :(得分:2)
在向量中使用智能指针,或使用boost的ptr_vector。它会自动释放其内部分配的对象。还有地图,集等等。
http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/ptr_vector.html 和主要网站: http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/ptr_container.html
答案 6 :(得分:0)
与堆中的任何其他对象一样,必须手动销毁它(使用delete)。
答案 7 :(得分:0)
回答你的第一个问题:
STL课程没什么特别的(我希望)。它们的功能与其他模板类完全相同。因此,如果在堆上分配它们,它们不会被自动销毁,因为C ++上没有垃圾收集(除非你告诉它与一些花哨的autoptr业务或其他东西)。如果你在堆栈上分配它(没有新的),它很可能会自动由C ++管理。
关于第二个问题,这是一个非常简单的ArrayOfTen类,用于演示C ++中典型内存管理的基础知识:
/* Holds ten Objects. */
class ArrayOfTen {
public:
ArrayOfTen() {
m_data = new Object[10];
}
~ArrayOfTen() {
delete[] m_data;
}
Object &operator[](int index) {
/* TODO Range checking */
return m_data[index];
}
private:
Object *m_data;
ArrayOfTen &operator=(const ArrayOfTen &) { }
};
ArrayOfTen myArray;
myArray[0] = Object("hello world"); // bleh
基本上,ArrayOfTen类在堆上保留十个Object元素的内部数组。当在构造函数中调用new []时,在堆上分配十个对象的空间,并构造十个对象。 Simiarly,当在析构函数中调用delete []时,十个对象被解构,然后释放先前分配的内存。
对于大多数(所有?)STL类型,调整大小是在幕后完成的,以确保有足够的内存设置以适应您的元素。上面的类只支持十个对象的数组。它基本上是Object [10]的一个非常有限的typedef。
答案 8 :(得分:0)
要删除指向的元素,我写了一个简单的仿函数:
template<typename T>
struct Delete {
void operator()( T* p ) const { delete p; }
};
std::vector< MyType > v;
// ....
std::for_each( v.begin(), v.end(), Delete<MyType>() );
但是当向量的内容是......嗯......共享时,你应该回退共享指针。是。
答案 9 :(得分:0)
答案 10 :(得分:0)
标准STL容器使用复制构造函数将原始对象的副本放入容器中。当容器被销毁时,也会调用容器中每个对象的析构函数来安全地销毁对象。
指针的处理方式相同。
事情是指针是POD数据。指针的复制构造函数只是复制地址而POD数据没有析构函数。如果您希望容器管理指针,您需要:
我更喜欢指针容器:
指针容器与STL容器相同,除了你将指针放入它们,但容器然后取得指针指向的对象的所有权,因此当容器被销毁时将释放对象(通常通过调用delete)。
当您访问ptr容器的成员时,它们通过引用返回,因此它们的行为就像标准容器中使用的标准容器一样。
int main()
{
boost::ptr_vector<int> data;
data.push_back(new int(5));
data.push_back(new int(6));
std::cout << data[0] << "\n"; // Prints 5.
std::cout << data[1] << "\n"; // Prints 6.
} // data deallocated.
// This will also de-allocate all pointers that it contains.
// by calling delete on the pointers. Therefore this will not leak.
还应该指出容器中的智能指针是一种有效的替代方法,不幸的是std :: auto_ptr&lt;&gt;对于这种情况,它不是智能指针的有效选择。
这是因为STL容器假设它们包含的对象是可复制的,不幸的是std :: auto_ptr&lt;&gt;在传统意义上是不可复制的,因为它会破坏副本上的原始值,因此副本的来源不能是常量。
答案 11 :(得分:0)
STL容器就像任何其他对象一样,如果你实例化它在堆栈上创建的那个:
std::vector<int> vec(10);
就像任何其他堆栈变量一样,它只存在于其定义的函数范围内,不需要手动删除。 STL容器的析构函数将调用容器中所有元素的析构函数。
在容器中保持指针是个棘手的问题。由于指针没有析构函数,我想你永远不会想把原始指针放到STL容器中。以异常安全的方式执行此操作将非常困难,您必须使用try {} finally {}块来丢弃代码,以确保始终释放包含的指针。
那么你应该把什么放入容器而不是原始指针? +1 jmucchiello用于提升boost :: shared_ptr。 boost :: shared_ptr在STL容器中使用是安全的(与std :: auto_ptr不同)。它使用简单的引用计数机制,可以安全地用于不包含循环的数据结构。
对于包含周期的数据结构,您需要什么?在这种情况下,您可能希望升级到垃圾收集,这实际上意味着使用不同的语言,如Java。但这是另一个讨论。 ;)