0

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;
}
  • 11
    You are using `new` *way too much*. There is pretty much never a reason to do `* new Car("BMW")`. – Nicol Bolas Feb 25 '16 at 06:05
  • 3
    Don't. Seriously, just don't. For now, just forget that you ever even heard of `new`. You don't need it and you don't want it. The array form of new expression is particularly bad--even in some future where you really need to use `new`, you shouldn't use an array `new`--ever. If you want a dynamic array, use `std::vector`. If you need a dynamic array of pointers (also unlikely), look up Boost [`ptr_vector`](http://www.boost.org/doc/libs/1_60_0/libs/ptr_container/doc/ptr_vector.html) and/or use a `vector`. Oh, and `main` returns an `int`. – Jerry Coffin Feb 25 '16 at 06:26
  • 1
    You use pointers too much. Here is an example where creating a vector on the heap/free store and then later deleting it using the destructor: – Andreas DM Feb 25 '16 at 06:38
  • Setting the pointer to null just before it goes out of scope doesn't do anything useful. – Pete Becker Feb 25 '16 at 14:34

9 Answers9

4

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.

The Dark
  • 8,453
  • 1
  • 16
  • 19
  • `Car *myArray = new Car[]{ * new Car..` is at best a pointer to an array of pointers. Meaning that `Car *myArray` is wrong in the first place. Besides I don't think you can statically allocate an array of objects with dynamically created content that way. You have to create the array first, then create the elements. `Car **myArray= new (Car **)[3]; myArray[0]=new Car("BMW"); ...` – Aslak Berby Feb 25 '16 at 09:04
  • @AslakBerby: That makes no sense at all. `Car *myArray` is a pointer to one or more cars, not a pointer to pointer. `new Car[ ]` creates an array of cars, not an array of pointers. And `*new Car("BMW")` is a `Car` object, not a pointer. it's also a memory leak, but that's unrelated. – MSalters Feb 25 '16 at 09:22
  • @MSalters The construct `new Car[n]` creates a array of Car. new `Car[]={new Car()}` is illegal, but in my head is a pointer to pointers. The correct syntax for this would be `Car * myArray[] = {new Car("BMW"),...};` – Aslak Berby Feb 25 '16 at 09:28
  • @AslakBerby: All true, but your first comment is about a different form, which in fact is **not** an pointer to an array of pointers. – MSalters Feb 25 '16 at 09:32
  • @MSalters: I see that I explained my self badly. – Aslak Berby Feb 25 '16 at 09:35
  • @AslakBerby you are correct that `Car * myArray = new Car[]{ ... };` is incorrect - my compiler let it through, then the program crashed on exit as it didn't allocated enough memory for the array. R Sahu already pointed that out in his answer. I will update mine to say the same. – The Dark Feb 25 '16 at 23:10
3

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.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 1
    `Car * myArray = new Car[3];` This is not going to compile in the first place since the OP has not defined a default constructor for `Car` – pcodex Feb 25 '16 at 06:43
1

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

Community
  • 1
  • 1
pcodex
  • 1,812
  • 15
  • 16
  • that's because the OP should have done that "something different". added more code to clarify my answer – pcodex Feb 25 '16 at 06:53
  • Works a lot better. Only problem left is an array is not a pointer, but that's so off topic that it doesn't matter here. Good reading on the subject: http://stackoverflow.com/questions/1641957/is-an-array-name-a-pointer-in-c – user4581301 Feb 25 '16 at 06:57
  • `"left is an array is not a pointer"` I better not even attempt to interpret what you're trying to say here. `"Works a lot better"` I never improved any code for it to work better. This is the only snippet I wrote and ofcourse it works. – pcodex Feb 25 '16 at 12:45
1
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.

Jts
  • 3,447
  • 1
  • 11
  • 14
0

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.

Robert Hegner
  • 9,014
  • 7
  • 62
  • 98
0

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.

Andreas DM
  • 10,685
  • 6
  • 35
  • 62
  • 1
    new std::vector is so wrong, please don't do that. You're doing one step in the right direction, so please just so std::vector. Then you don't even need to define the destructor. – stefan Feb 25 '16 at 07:19
  • @stefan yes, you're right, but it's only for demonstration – Andreas DM Feb 25 '16 at 07:20
  • Especially as a demonstration for beginners, it's valuable to not have new anywhere near sample code ;-) – stefan Feb 25 '16 at 07:22
0

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));
Aslak Berby
  • 183
  • 1
  • 7
0

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

djgandy
  • 1,125
  • 6
  • 15
0

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;

Wael Assaf
  • 1,233
  • 14
  • 24