Kenneth Andersen
Kenneth Andersen

Reputation: 63

C++ Is delete[] enough to clean up after an array?

In the code below I'm allocating an array dynamically by using the new keyword. Before I run out of scope I call delete[] on my array, and afterwards I set the punter to null.

My Question is, if this is enough, will delete[] make sure that the allocated memory for all 3 Car objects in my array is released. Or do I have to do something specific to release the memory used by every single object?

void main()
{
    Car * myArray = new Car[]{ * new Car("BMW"),*new Car("AUDI"), * new Car("SKODA") };

    delete[] myArray;
    myArray = nullptr;
}

Also, the car class looks like this. Is it also enough to set the name to null here. Name is a char pointer. Or maybe it isn't needed to set the pointer to null since it isn't referencing anything on the heap.

Car::Car(char * newName)
{
    name = newName;
}

Car::~Car()
{
    name = nullptr;
}

EDIT:

First of all, thanks for all the great answers and comments. I learned a lot from reading them.

Now I understand, that I need to specify a size when declaring a dynamic allocated array.

Besides that I also understand, that I need to stop using new as much as I do. I guess its better to throw the objects on the stack, and let them go out of scope at some point. Besides that I guess my destructor on my car does nothing.

After reading the comments and the answers, I'v change my code to this:

int main()
{
    Car * myArray = new Car[3]{ Car("BMW"), Car("AUDI"), Car("SKODA") };

    delete[] myArray;
    myArray = nullptr;
}

Upvotes: 0

Views: 6393

Answers (9)

Wael Assaf
Wael Assaf

Reputation: 1303

No. delete []myArray Will merely result in something called a dangling pointer.

Which basically means that the program no longer owns the memory pointed to myArray, but myArray still points to that memory.

To avoid dangling pointers assign your pointer to nullptr after deletion.

delete[]myArray;

myArray = nullptr;

Upvotes: 0

djgandy
djgandy

Reputation: 1135

I am not sure how I ended up here on a two year old post. None of the answers really give a simple C++ solution so here is a 60 second solution using containers and no new/delete in sight.

#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

Upvotes: 0

The Dark
The Dark

Reputation: 8514

In your case, you have already leaked the memory from your calls to new Car("BMW") and have lost the pointer to be able to free the memory.

This line:

Car * myArray = new Car[]{ * new Car("BMW"),*new Car("AUDI"), * new Car("SKODA") };

Creates an array of 3 Car objects, then for each entry it creates a new Car object, uses it to initialize the object in the array, then forgets about the new object.

It can be more simply written like this:

Car * myArray = new Car[3]{ Car("BMW"), Car("AUDI"), Car("SKODA") };

or even

Car * myArray = new Car[3]{ "BMW", "AUDI", "SKODA" };

In which case your delete[] is enough to free up the memory used.

Note that

Car::~Car()
{
    name = nullptr;
}

does not do anything to free memory. Once the Car destructor is called, no one should be accessing name for that object again (in fact it is undefined behavior), so there is little point in setting it to null.

Edit Note: As pointed out by R Sahu and Aslak Berby, Car * myArray = new Car[]{ ... }; is not a valid way to make an array, use Car * myArray = new Car[3]{ ... }; instead.

Upvotes: 4

Aslak Berby
Aslak Berby

Reputation: 183

You can add a object to the heap or the stack. If you add it to the heap you create it dynamicaly as you go. This is done using new and you get a pointer in return.

Car *aCar=new Car("BMW");

If you create it on the stack, you will just define it as you do with other variables.

Car anotherCar("BMW");

If you create it on the heap, you also need to deallocate it from the heap. This is done with delete.

delete aCar;

You never dealocate a object you created on the stack. That will automtically be dealocated when you go out of scope.

As for creating a array, you can create a array of statick or dynamicall objects.

Dynamical:

Car **cars=new Car*[3];
cars[0]=new Car("BMW");
cars[1]=new Car ....

All of those need to be deleted seperatly. No cascading here.

delete cars[0];
delete cars[1];
// And finaly the array.
delete[] cars;

You can create them staicaly:

Car cars[]={"BWM", "AUDI"};

This time all the objects including the array is pushed to the stack and will be deleted when you go out of scope.

In between you can create stuff that is a stackbased array that points to heap allocated objects, or a heapallocated static array as other suggest here.

As for C++ I would suggest using the std:: library and in this case std::vector;

Either:

std::vector<Car *> myArray;
myArray.push_back(new Car("BMW"));

.....
// Cleanup:
for(auto car : myArray)
     delete car;

Or:

Car car;    
std::vector<Car> myArray;

car.SetType("BMW");
myArray.push_back(std::move(car));

Upvotes: 0

Andreas DM
Andreas DM

Reputation: 11028

I agree with the comments that you are using the keyword new too much.

I suggest using std::vector instead.

Here is a working example where the class Garage has a vector of Car's on the heap/freestore and later deleting it with destructor ~Garage

Example for illustration only:

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();
}

Using new vector above is only for demonstration, it's not needed. Using a std::vector without new is faster and safer for this purpose and you won't need to delete it after use.

Also consider using Smart Pointers instead of using new.

Upvotes: 0

pcodex
pcodex

Reputation: 1950

yes it is sufficient only if you are creating a plain array of Car elements since an array name is a pointer to its first element

You are informing the compiler that its an array by specifying the []

In your case you seem to be creating car pointers so you have to clean up the memory location occupied by each car and then the memory allocated for the whole array.

What you incorrectly attempted to do is this but don't do it. Its convoluted

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;

Take a look at this discussion

delete[] an array of objects

Upvotes: 1

R Sahu
R Sahu

Reputation: 206727

You cannot use:

Car * myArray = new Car[]{ * new Car("BMW"),*new Car("AUDI"), * new Car("SKODA") };

You need to specify a size.

Car * myArray = new Car[3]{ * new Car("BMW"),*new Car("AUDI"), * new Car("SKODA") };

Even after that, calling

delete [] myArrary;

is going to leak memory. That line is equivalent to:

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;

The line

delete [] myArrary;

does not delete the objects allocated separately for car1, car2, and car3. You'll have to explicitly delete those too.

delete car3;
delete car2;
delete car1;

However, you cannot use

Car * myArray = new Car[3];

since Car does no have a default constructor. You can add a default constructor to Car. Failing that you can to use:

Car * myArray = new Car[3]{ Car("BMW"), Car("AUDI"), Car("SKODA") };

Then, it is sufficient to use:

delete [] myArrary;

to deallocate the memory.

Upvotes: 3

Jts
Jts

Reputation: 3527

Car * myArray = new Car[X];

This code already creates X Car objects. All you have to do is use them really..

However, I think your confussion lies here: this is another approach to do it

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;

This code allocates an array of 3 Car* pointers. Therefore, you have not created any Car object yet, which is why you initialize each Car* pointer with with a new Car() call, which actually creates the Car object.

Upvotes: 1

Robert Hegner
Robert Hegner

Reputation: 9396

Basically you need a delete (or delete[]) for every new. But in a more complex program this can be very difficult (and error prone) to assure.

Instead of raw pointers you should learn to use smart pointers like shared_ptr or unique_ptr. These let you avoid explicit new and delete in most cases.

Upvotes: 0

Related Questions