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;

不会删除为car1car2car3 单独分配的对象。您也必须明确删除它们。

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_ptrunique_ptr。在大多数情况下,这些可以让您避免显式的 newdelete

【讨论】:

【参考方案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。那么你甚至不需要定义析构函数。 @stefan 是的,你是对的,但这只是为了演示 特别是作为初学者的演示,在示例代码附近没有新的东西很有价值;-)【参考方案7】:

您可以将对象添加到堆或堆栈中。如果将其添加到堆中,则可以随时动态创建它。这是使用 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[] 是不是足以在数组后清理?的主要内容,如果未能解决你的问题,请参考以下文章

delete[ ] 是不是会深度删除 C++ 中的结构?

c++中new和delete的用法

在 POD 数组上使用 delete 而不是 delete[] 是不是安全? [复制]

C++中Delete用法:请问int *p=new int(5); 此时delete的形式是delete []p还是delete p;

在 C++ 中的 2D 动态内存分配数组中释放分配的内存

C++ 中的 delete vs delete[] 运算符