C++ delete[] 是不是足以在数组后清理?
Posted
技术标签:
【中文标题】C++ delete[] 是不是足以在数组后清理?【英文标题】:C++ Is delete[] enough to clean up after an array?C++ delete[] 是否足以在数组后清理? 【发布时间】:2016-02-25 06:04:59 【问题描述】:在下面的代码中,我使用 new 关键字动态分配数组。在超出范围之前,我在数组上调用 delete[],然后将下注设置为 null。
我的问题是,如果这足够了,delete[] 会确保为我的数组中的所有 3 个 Car 对象分配的内存都已释放。还是我必须做一些特定的事情来释放每个对象使用的内存?
void main()
Car * myArray = new Car[] * new Car("BMW"),*new Car("AUDI"), * new Car("SKODA") ;
delete[] myArray;
myArray = nullptr;
另外,汽车类看起来像这样。在这里将名称设置为空是否也足够了。名称是一个字符指针。或者可能不需要将指针设置为 null,因为它没有引用堆上的任何内容。
Car::Car(char * newName)
name = newName;
Car::~Car()
name = nullptr;
编辑:
首先,感谢所有出色的答案和 cmets。我从阅读中学到了很多东西。
现在我明白了,我需要在声明一个动态分配的数组时指定一个大小。
除此之外,我也明白,我需要像我一样停止使用 new。我想最好将对象扔到堆栈上,并在某个时候让它们超出范围。除此之外,我猜我的汽车上的析构函数什么也没做。
阅读 cmets 和答案后,我将代码更改为:
int main()
Car * myArray = new Car[3] Car("BMW"), Car("AUDI"), Car("SKODA") ;
delete[] myArray;
myArray = nullptr;
【问题讨论】:
你使用new
太多了。几乎没有理由这样做* new Car("BMW")
。
不要。说真的,只是不要。现在,忘记你甚至听说过new
。你不需要它,你也不想要它。 new 表达式的数组形式特别糟糕——即使在将来你真的需要使用new
,你也不应该使用数组new
——永远。如果您想要一个动态数组,请使用std::vector
。如果您需要一个动态指针数组(也不太可能),请查找 Boost ptr_vector
和/或使用 vector<unique_ptr>
。哦,main
返回一个 int
。
你使用指针太多了。这是一个在堆/空闲存储上创建向量然后使用析构函数将其删除的示例:
在指针超出范围之前将其设置为 null 并没有任何用处。
【参考方案1】:
在您的情况下,您已经从对new Car("BMW")
的调用中泄漏了内存,并且丢失了能够释放内存的指针。
这一行:
Car * myArray = new Car[] * new Car("BMW"),*new Car("AUDI"), * new Car("SKODA") ;
创建一个包含 3 个 Car
对象的数组,然后为每个条目创建一个新的 Car
对象,使用它来初始化数组中的对象,然后忘记新对象。
可以更简单的写成这样:
Car * myArray = new Car[3] Car("BMW"), Car("AUDI"), Car("SKODA") ;
甚至
Car * myArray = new Car[3] "BMW", "AUDI", "SKODA" ;
在这种情况下,您的delete[]
足以释放所使用的内存。
注意
Car::~Car()
name = nullptr;
不做任何事情来释放内存。一旦调用了Car
析构函数,就没有人应该再次访问该对象的name
(实际上它是未定义的行为),因此将其设置为null 没有什么意义。
编辑说明:正如 R Sahu 和 Aslak Berby 所指出的,Car * myArray = new Car[] ... ;
不是创建数组的有效方法,请改用Car * myArray = new Car[3] ... ;
。
【讨论】:
Car *myArray = new Car[] * new Car..
充其量是一个指向指针数组的指针。这意味着 Car *myArray
首先是错误的。此外,我认为您不能以这种方式静态分配具有动态创建内容的对象数组。您必须先创建数组,然后再创建元素。 Car **myArray= new (Car **)[3]; myArray[0]=new Car("BMW"); ...
@AslakBerby:这完全没有意义。 Car *myArray
是指向一辆或多辆汽车的指针,而不是指向指针的指针。 new Car[ ]
创建汽车数组,而不是指针数组。而*new Car("BMW")
是Car
对象,而不是指针。这也是内存泄漏,但这无关紧要。
@MSalters 构造 new Car[n]
创建了一个 Car 数组。 new Car[]=new Car()
是非法的,但在我的脑海中是一个指向指针的指针。正确的语法是Car * myArray[] = new Car("BMW"),...;
@AslakBerby:都是真的,但您的第一条评论是关于另一种形式的,实际上它不是指向指针数组的指针。
@MSalters:我发现我解释得很糟糕。【参考方案2】:
你不能使用:
Car * myArray = new Car[] * new Car("BMW"),*new Car("AUDI"), * new Car("SKODA") ;
您需要指定尺寸。
Car * myArray = new Car[3] * new Car("BMW"),*new Car("AUDI"), * new Car("SKODA") ;
即使在那之后,调用
delete [] myArrary;
将泄漏内存。该行相当于:
Car * myArray = new Car[3];
Car* car1 = new Car("BMW");
Car* car2 = new Car("AUDI");
Car* car3 = new Car("SKODA");
myArray[0] = *car1;
myArray[1] = *car2;
myArray[2] = *car3;
线
delete [] myArrary;
不会删除为car1
、car2
和car3
单独分配的对象。您也必须明确删除它们。
delete car3;
delete car2;
delete car1;
但是,你不能使用
Car * myArray = new Car[3];
因为Car
没有默认构造函数。您可以将默认构造函数添加到Car
。失败了你可以使用:
Car * myArray = new Car[3] Car("BMW"), Car("AUDI"), Car("SKODA") ;
那么,使用就足够了:
delete [] myArrary;
释放内存。
【讨论】:
Car * myArray = new Car[3];
这首先不会编译,因为 OP 没有为 Car
定义默认构造函数【参考方案3】:
Car * myArray = new Car[X];
这段代码已经创建了X
Car 对象。您所要做的就是真正使用它们..
但是,我认为您的困惑在于:这是另一种方法
Car ** myArray = new Car*[3] new Car("BMW"), new Car("AUDI"), new Car("SKODA") ;
for (int i = 0; i < 3; i++)
delete myArray[i];
delete[] myArray;
这段代码分配了一个由 3 个Car*
指针组成的数组。因此,您尚未创建任何 Car 对象,这就是您使用 new Car()
调用初始化每个 Car*
指针的原因,这实际上创建了 Car 对象。
【讨论】:
【参考方案4】:是的,仅当您创建 Car
元素的普通数组时才足够,因为数组名称是指向其第一个元素的指针
您通过指定 []
来通知编译器它是一个数组
在您的情况下,您似乎正在创建汽车指针,因此您必须清理每辆汽车占用的内存位置,然后清理为整个数组分配的内存。
您错误地尝试这样做,但不要这样做。复杂的
Car** cararrptr = new Car*[3];
cararrptr[0] = new Car("win");
cararrptr[1] = new Car("lin");
cararrptr[2] = new Car("ios");
//now to clean up
delete cararrptr[0];
delete cararrptr[1];
delete cararrptr[2];
delete[] cararrptr;
看看这个讨论
delete[] an array of objects
【讨论】:
那是因为 OP 应该做“一些不同的事情”。添加了更多代码来澄清我的答案 效果更好。剩下的唯一问题是数组不是指针,但这太离题了,在这里无关紧要。关于这个主题的好读物:***.com/questions/1641957/…"left is an array is not a pointer"
我最好不要试图解释你在这里想说什么。 "Works a lot better"
我从未改进任何代码以使其更好地工作。这是我写的唯一一个 sn-p,它当然有效。【参考方案5】:
基本上,每个new
都需要一个delete
(或delete[]
)。但在更复杂的程序中,这可能非常难以保证(并且容易出错)。
您应该学习使用智能指针,而不是原始指针,例如 shared_ptr
或 unique_ptr
。在大多数情况下,这些可以让您避免显式的 new
和 delete
。
【讨论】:
【参考方案6】:我同意 cmets 的观点,即您过多地使用关键字 new
。
我建议改用std::vector
。
这是一个工作示例,其中类 Garage
在堆/freestore 上有一个 Car
的向量,然后使用析构函数 ~Garage
将其删除
示例仅供说明:
class Car
Car();
string m_name;
public:
Car(const string &);
void print();
;
Car::Car(const string &s) : m_names
void Car::print()
cout << m_name << endl;
class Garage
string m_name;
vector<Car> *m_cars; // for allocating on heap
public:
Garage(const string &);
~Garage();
void add(const Car &);
void print();
;
// Creating a new vector on heap
Garage::Garage(const string &s) : m_names, m_carsnew vector<Car>
Garage::~Garage()
delete m_cars; // deleting the vector.
void Garage::add(const Car &c)
m_cars->push_back(c);
void Garage::print()
for (auto car : *m_cars)
car.print();
int main()
Garage garage"Luxury garage";
garage.add(Car("BMW"));
garage.add(Car("Audi"));
garage.add(Car("Skoda"));
garage.print();
上面使用新向量只是为了演示,不需要。为此,使用不带 new 的 std::vector
更快、更安全,并且您无需在使用后将其删除。
还可以考虑使用智能指针而不是new
。
【讨论】:
new std::vector 太错误了,请不要那样做。您正在朝着正确的方向迈出一步,所以请 std::vector您可以将对象添加到堆或堆栈中。如果将其添加到堆中,则可以随时动态创建它。这是使用 new 完成的,你会得到一个指针作为回报。
Car *aCar=new Car("BMW");
如果您在堆栈上创建它,您只需像使用其他变量一样定义它。
Car anotherCar("BMW");
如果你在堆上创建它,你还需要从堆中释放它。这是通过删除完成的。
delete aCar;
你永远不会释放你在堆栈上创建的对象。当您超出范围时,它将自动被释放。
至于创建数组,可以创建statick或dynamicall对象的数组。
动态:
Car **cars=new Car*[3];
cars[0]=new Car("BMW");
cars[1]=new Car ....
所有这些都需要单独删除。这里没有级联。
delete cars[0];
delete cars[1];
// And finaly the array.
delete[] cars;
您可以稳定地创建它们:
Car cars[]="BWM", "AUDI";
这次包括数组在内的所有对象都被推入堆栈,当你超出范围时将被删除。
在这两者之间,您可以创建指向堆分配对象的基于堆栈的数组,或其他人建议的堆分配静态数组。
对于 C++,我建议使用 std:: 库,在本例中为 std::vector;
要么:
std::vector<Car *> myArray;
myArray.push_back(new Car("BMW"));
.....
// Cleanup:
for(auto car : myArray)
delete car;
或者:
Car car;
std::vector<Car> myArray;
car.SetType("BMW");
myArray.push_back(std::move(car));
【讨论】:
【参考方案8】:我不确定我是如何在一个两年前的帖子上结束的。没有一个答案真正给出了一个简单的 C++ 解决方案,所以这里是一个使用容器的 60 秒解决方案,并且看不到新/删除。
#include <iostream>
#include <string>
#include <vector>
struct Car
// Allows implicit conversion from char *
Car(const char *manufacturer) : manufacturer_(manufacturer)
std::string manufacturer_;
;
int main()
std::vector<Car> cars "BMW", "AUDI", "SKODA" ;
for (const auto& car : cars)
std::cout << car.manufacturer_ << "\n";
live demo
【讨论】:
【参考方案9】:不会。 delete []myArray
只会产生一个叫做悬空指针的东西。
这基本上意味着程序不再拥有指向的内存 myArray,但 myArray 仍然指向那个内存。
为避免悬空指针在删除后将指针分配给nullptr
。
delete[]myArray;
myArray = nullptr;
【讨论】:
以上是关于C++ delete[] 是不是足以在数组后清理?的主要内容,如果未能解决你的问题,请参考以下文章
在 POD 数组上使用 delete 而不是 delete[] 是不是安全? [复制]
C++中Delete用法:请问int *p=new int(5); 此时delete的形式是delete []p还是delete p;