我正在尝试创建类数据类型Pool
的指针数组,但删除它就好像它是一个数组一样不起作用。
这是代码:
struct Pool {
/* in reality, complicated stuff here */
int size;
};
Pool* createLargePool() { return new Pool{100}; }
Pool* createMediumPool() { return new Pool{20}; }
Pool* createSmallPool() { return new Pool{5}; }
int main() {
using namespace std;
enum ePool{
small,
medium,
large,
last
};
Pool *pools[ePool::last] = {
createLargePool()
, createMediumPool()
, createSmallPool()
};
//Individually works.
delete pools[0];
delete pools[1];
delete pools[2];
//delete[] pools;// error. (assertion error?)
system("pause");
return 0;
}
根据我的理解,我的行Pool *pools[]
正在创建一个Pool指针数组。这正是我的IDE所说的我正在做的事情。在我到达删除声明之前,我没有遇到任何问题。
由于某种原因delete[]
导致我出现问题,但是单独删除每个问题不会:
发生了什么以及如何让删除[]工作?
答案 0 :(得分:7)
使用单个命令删除每个元素的唯一方法是将其设为std::vector<std::unique_ptr<Pool>>
并调用pools.clear()
。
所有其他解决方案都要求您循环遍历元素并单独删除它们。
此外,您的create*Pool()
函数不应返回原始指针。他们应该返回智能指针,原因有两个:
然后,用户知道他们负责删除指针。
(使用原始指针,他们必须查看内部实现和/或文档才能知道。)
这使得内存泄漏几乎不可能,因为用户现在不必记得在某个地方调用delete
,这在复杂的项目中很难正确。
所以我建议您返回std::unique_ptr
:
std::unique_ptr<Pool> createLargePool() { return std::unique_ptr<Pool>(new Pool{100}); }
或更好(在C ++ 14中):
std::unique_ptr<Pool> createLargePool() { return std::make_unique<Pool>(100); }
现在,即使您将智能指针存储在原始静态数组中:
std::unique_ptr<Pool> pools[ePool::last] = {
createLargePool()
, createMediumPool()
, createSmallPool()
};
当数组超出范围时,它们将delete
它们的托管指针。
答案 1 :(得分:4)
&#34;发生了什么以及如何让
delete[]
工作?&#34;
当然delete[]
或delete
没有先前的new[]
或new
声明无效。
如果内存未动态分配,则调用delete
的任一形式,引发未定义的行为。
答案 2 :(得分:4)
如果您要创建delete[]
的动态数组,则可以使用Pool
。但是你在这里做的事情(在我看来)正在创建一个指向动态Pool
的静态指针数组。所以,而不是delete[]
整个事情就好像它是一个动态数组,它不是,你需要做的是delete
数组中的每个单独指针,就像你一样不经意间发现了。
但请注意,明确删除每个元素都是冗长而脆弱的;最好像这样循环遍历:
for (int i = 0; i < ePool::last; ++i)
delete pools[i];
或者更好,像这样:
for (Pool *p: pools)
delete p;
答案 3 :(得分:4)
根据我的理解,我的行
Pool *pools[]
正在创建一个Pool指针数组。
正确。具体来说,是一个3个Pool*
指针的本地静态数组。
由于某些原因
delete[]
导致我出现问题,但单独删除每个问题
您没有使用new[]
分配数组,因此您无法使用delete[]
释放它。它存在于堆栈上,而不是堆上,因此当数组超出范围时将被回收。
但是,您的数组元素是指向动态分配的Pool
个对象的指针,无论数组是如何分配的,都需要使用delete
释放这些对象。即使数组是使用new[]
动态分配的,在使用delete
释放数组之前,您仍然需要Pool
个别delete[]
个对象。
发生了什么以及如何让
delete[]
工作?
如果您真的想使用delete[]
,则必须使用new[]
:
Pool** pools = new Pool*[ePool::last];
pools[0] = createLargePool(Vector3i(12, 45));
pools[1] = createMediumPool(Vector3i(12, 45));
pools[2] = createSmallPool(Vector3i(12, 45));
...
delete pools[0];
delete pools[1];
delete pools[2];
delete[] pools;
否则,您应该使用std::vector
代替new[]
:
std::vector<Pool*> pools(ePool::last);
pools[0] = createLargePool(Vector3i(12, 45));
pools[1] = createMediumPool(Vector3i(12, 45));
pools[2] = createSmallPool(Vector3i(12, 45));
或者:
std::vector<Pool*> pools;
pools.reserve(ePool::last);
pools.push_back(createLargePool(Vector3i(12, 45)));
pools.push_back(createMediumPool(Vector3i(12, 45)));
pools.push_back(createSmallPool(Vector3i(12, 45)));
要释放池,您可以使用循环:
// C++11 and later only...
for (Pool* pool : pools)
delete pool;
或者:
for (int i = ePool::small; i < ePool::last; ++i)
delete pools[i];
或者:
for (std::vector<Pool*>::iterator iter = pools.begin(); iter != pools.end(); ++iter)
delete *iter;
或者:
void freePool(Pool *pool)
{
delete pool;
}
std::for_each(pools.begin(), pools.end(), freePool);
如果您使用的是C ++ 11,则可以使用std::unique_ptr<Pool>
代替Pool*
作为std::vector
元素,并取消手动调用delete
std::auto_ptr
在STL容器中使用是不安全的,所以不要尝试):
std::vector<std::unique_ptr<Pool>> pools;
...
但是,由于您有一小部分池,因此std::unique_ptr<Pool>
(C ++ 11及更高版本)或std::auto_ptr<Pool>
元素的数组开销将小于std::vector
:
#include <memory>
int main()
{
std::unique_ptr<Pool> pools[ePool::last];
pools[ePool::small] = createSmallPool();
pools[ePool::medium] = createMediumPool();
pools[ePool::large] = createLargePool();
/*
std::auto_ptr<Pool> pools[ePool::last];
pools[ePool::small].reset(createSmallPool());
pools[ePool::medium].reset(createMediumPool());
pools[ePool::large].reset(createLargePool());
*/
...
system("pause");
return 0;
}
顺便说一句,顺便提一下,您正在按向后顺序创建池。您的枚举按小/中/大订购,但您的阵列按大/中/小订购。您需要交换createLargePool()
和createSmallPool()
,如上所示。
答案 4 :(得分:1)
如评论中所述,您无法在未使用new []在免费商店上分配的数组上调用delete []。但是,要回答您提出的问题的其他方面,有一种更好的方法可以自动解除所有内容。使用std::vector<Pool> pools
而不是数组,对于每个池,只需执行pools.push_back( createMediumPool(Vector3i(12, 45)) )
等...
然后,当池超出范围时,它会自动调用它包含的每个池的析构函数,并且所有解除分配都将自动发生。作为一个额外的好处,如果你在某个时候抛出错误并采取代码的“不愉快路径”,它仍然会自动释放所有资源。这是资源分配初始化(RAII)原则。
答案 5 :(得分:0)
您的数组已在堆栈中分配,无需删除。基于堆栈的变量不必是,实际上不应该被删除。您只能使用delete来清理基于堆的对象。举个例子(假设你有一个典型的32位系统,那个sizeof(int)== 4个字节)
void foo() {
int x = 10; // x on stack. consumes 4 bytes
int y[3] = { 29, 42, 17}; // y on stack. consumes 12 bytes
int z = 11; // z on stack. another 4 bytes
cout << "Address of x = " << &x << endl;
cout << "Address of y[0] = " << &y[0] << endl;
cout << "Address of y[1] = " << &y[1] << endl;
cout << "Address of y[2] = " << &y[2] << endl;
cout << "Address of z = " << &z << endl;
delete x; // NO! Don't delete a stack based variable
delete y; // NO! Don't delete him either
delete z; // NO! What's wrong with you.
}
编译器生成用于管理基于堆栈的变量的代码。 Spaced在函数开始时在堆栈上分配,并在函数返回时回收。
堆上的对象遵循不同的生命周期规则。完成后,您可以删除它们。
void foo() {
int *x = new int; // x points to a 4 byte memory block on the heap
int *y = new int[3]; // y points to a 12 byte memory block on the heap
delete x; // Yes. Thank you.
delete []y; // Correct.
}
您的代码示例有点复杂,因为您有一个基于堆栈的数组,它包含基于堆的对象。您需要删除数组中的各个基于堆的对象,而不是数组本身。
这是一个显示各种口味的例子
struct Pool {
int x;
};
int main() {
Pool p1[2] = {1,2} ; // array of objects. Everything lives on the stack.
Pool* p2[2] = {new Pool, new Pool} ; // array of pointers. Array on stack. Pools in it live on the heap
Pool *p3 = new Pool[2]; // heap based array of objects.
p3[0].x=1;
p3[1].x=7;
Pool **p4 = new Pool*[2]; // heap based array of pointers to objects.
p4[0] = new Pool;
p4[1] = new Pool;
// cleanup
// none for p1
delete p2[0]; // delete the heap based objects inside p2
delete p2[1];
delete [] p3; // delete the heap of objects
delete p4[0]; // delete pointers inside p4
delete p4[1];
delete []p4; // plus the heap based array;
return 0;
}