【问题标题】: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 并没有任何用处。

标签: c++ arrays pointers dynamic-memory-allocation


【解决方案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 应该做“一些不同的事情”。添加了更多代码来澄清我的答案
    • 效果更好。剩下的唯一问题是数组不是指针,但这太离题了,在这里无关紧要。关于这个主题的好读物:stackoverflow.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_name{s} { }
      
      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_name{s}, m_cars{new 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;

          【讨论】:

            猜你喜欢
            • 2022-12-16
            • 2020-09-12
            • 1970-01-01
            • 2011-02-25
            • 1970-01-01
            • 2011-10-31
            • 2014-04-29
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多