-1

I'm working on a program for a lab, and I need some help with the memory management. I'm new to C++ as a whole, and while I have experience in other languages, dynamic memory management is confusing me. Also, because this is for a lab, I can't use std::vector or smart pointers, just arrays and pointers.

First, there's a "Vehicle" class, with some attributes describing it (such as make, model, etc). Next, there's a "Showroom" class, which contains an array of Vehicles:

Vehicle * m_vehicles;

And later, in the constructor:

m_vehicles = new Vehicle[m_maxCapacity];

Next there's a "Dealership" class, which has an array of Showrooms:

Showroom * m_showrooms;
...
m_showrooms = new Showroom[m_maxCapacity];

My main() method creates some vehicles, adds those to showrooms, and then adds those to a dealership. Everything works without issue until the program ends. When the program finishes and the objects are deleted, some problems come up. As the dealership was created last, it is deleted first. It's destructor calls delete[] m_showrooms; , and the destructor in the Showroom class calls delete[] m_vehicles;. So, I expect that when the program ends, the OS deletes the dealership object, which removes the showroom objects, which in turn remove the vehicle objects.

However, it doesn't work properly. The lab requires that we use a helper that has been provided which marks memory leaks as errors. When my main() creates a showroom, adds vehicles to it, creates a dealership, and then adds the showroom to the dealership, when the program ends the memory leak detector gives an error, saying:

delete[] error: pointer was not allocated!

I get this error once for every showroom I add to the dealership. Also, there's another bizarre error: I get a segfault when the program ends if the dealership contains any two showrooms which have the same capacity.

If I say "screw it" and remove all of the delete[]'s from the destructors, the program runs without a segfault, but the memory leak program detects the leak and prevents me from moving on.

What am I doing wrong? Do I have some sort of fundamental misunderstanding of dynamic memory in C++? I read that every new[] should match with a delete[], and that's what I have. I've already asked my TA about this, and she didn't have any idea about how to solve it.

EDIT: Here's some relevant code:

main.cpp:

//array of vehicles to store
Vehicle vehicles[] =
        {
                Vehicle("Ford", "Mustang", 1973, 9500, 113000),
                Vehicle("Mazda", "CX-5", 2017, 24150, 5900),
                Vehicle("Dodge", "Charger", 2016, 18955, 9018),
                Vehicle("Telsa", "Model S", 2018, 74500, 31),
                Vehicle("Toyota", "Prius", 2015, 17819, 22987),
                Vehicle("Nissan", "Leaf", 2016, 12999, 16889),
                Vehicle("Chevrolet", "Volt", 2015, 16994, 12558),
         };
    // Showrooms to store the vehicles
        Showroom showroom("Primary Showroom",2);
        showroom.AddVehicle(&vehicles[0]);
        showroom.AddVehicle(&vehicles[1]);
        //showroom.AddVehicle(&vehicles[2]);

        Showroom secondary("Storeroom 2",4);
        secondary.AddVehicle(&vehicles[3]);
        secondary.AddVehicle(&vehicles[4]);
        secondary.AddVehicle(&vehicles[5]);
        secondary.AddVehicle(&vehicles[6]);

        // A "parent" object to store the Showrooms
        Dealership dealership("Dealership",2);
        dealership.AddShowroom(&showroom);
        dealership.AddShowroom(&secondary);
        //displays the showrooms and their contents
        dealership.ShowInventory();

Relevant functions from Vehicle.cpp:

Vehicle::Vehicle(std::string mk, std::string md, int yr, int pr, int ml) {
    make = mk;
    model = md;
    year = yr;
    price = pr;
    miles = ml;

}
Vehicle::~Vehicle() {}

Vehicle::Vehicle(const Vehicle &veh) {
    //year  = new int;
    make = veh.make;
    model = veh.model;
    year = veh.year;
    price = veh.price;
    miles = veh.miles;
}
Vehicle& Vehicle::operator=(const Vehicle &veh) {
    make = veh.make;
    model = veh.model;
    year = veh.year;
    price = veh.price;
    miles = veh.miles;
    return *this;
}
Vehicle::Vehicle() {}

From Showroom.cpp:

//copy constructor
Showroom::Showroom(const Showroom &s)
{
    m_name = s.m_name;
    m_maxCapacity =s.m_maxCapacity;
    m_currentNumberOfVehicles = s.m_currentNumberOfVehicles;
    m_vehicles = new Vehicle[m_maxCapacity];

    for (int i = 0; i< s.m_currentNumberOfVehicles;i++)
    {
        m_vehicles[i] = *new Vehicle(s.m_vehicles[i]);
    }
}
//normal constructor
Showroom::Showroom(std::string name, unsigned int maxCapacity) {
    m_name = name;
    m_maxCapacity = maxCapacity;
    m_vehicles = new Vehicle[m_maxCapacity];

    m_currentNumberOfVehicles = 0;

}
Showroom::~Showroom() {
    delete[] m_vehicles;

}
Showroom::Showroom(){}

From Dealership.cpp:

//copy constructor
Dealership::Dealership(const Dealership &d)
{
    m_name = d.m_name;
    m_maxCapacity =d.m_maxCapacity;
    m_currentNumberOfShowrooms = d.m_currentNumberOfShowrooms;
    m_showrooms = new Showroom[m_maxCapacity];

    for (int i = 0; i< d.m_currentNumberOfShowrooms;i++)
    {
        m_showrooms[i] = *new Showroom(d.m_showrooms[i]);
    }
}
//normal constructor
Dealership::Dealership(std::string name, unsigned int capacity) {
    m_name = name;
    m_maxCapacity = capacity;
    m_currentNumberOfShowrooms = 0;
    m_showrooms = new Showroom[m_maxCapacity];
}
Dealership::~Dealership() {
    //std::cout<<"Deleting dealership " <<m_name << std::endl;
    delete[] m_showrooms;
    //m_showrooms = 0;

}

EDIT 2: Here is the smallest amount of code in main() that gives and error saying that I tried to deallocate a pointer that wasn't allocated:

Showroom sh("Name", 0);

    Dealership dealer("dealer", 1);
    dealer.AddShowroom(&sh);

Here's the smallest amount of code that segfaults:

Showroom sh("Name", 0);
    Showroom sh2("Showroom 2",0);
    Dealership dealer("dealer", 2);

    dealer.AddShowroom(&sh);
    dealer.AddShowroom(&sh2);

And here's AddShowroom(), for reference:

void Dealership::AddShowroom(const Showroom *showroom) {

    m_showrooms[m_currentNumberOfShowrooms] = *showroom;
    m_currentNumberOfShowrooms++;
}
  • 7
    Instead of the verbose explanation, show your code. It does sound like you are not deleting your arrays properly (or maybe trying to delete them twice). – PlinyTheElder Sep 07 '18 at 00:05
  • 1
    Please provide your code. My random guess would be that you don't follow [The Rule of Three](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) but copy your objects. If you're allowed and have learnt about it, [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr) would likely solve everything. – Ken Y-N Sep 07 '18 at 00:07
  • `delete[]` what exactly are you deleting? – macroland Sep 07 '18 at 00:08
  • 3
    You likely are making a common mistake of writing the entire huge pile of code first, and then running it and see if it works. But if it doesn't, you really have no idea where the problem might be. Professional programmers don't write everything at once, then debug it. They write only a few lines of code at a time, then run it, test it, verify that it works, then write the next few lines of code. Start by writing a short program that allocates a few objects, uses them, then `delete`s them. Once you make sure it works correctly, then write some more code that creates a few more objects, etc... – Sam Varshavchik Sep 07 '18 at 00:10
  • A wild guess: Are you copying parent object which makes two objects to run their destructors(twice) with their child objects also copied from another so that they destruct their children twice? Since there are multiple levels, you may even be destructors for M*N times. Can you at least show us copy constructors please? – huseyin tugrul buyukisik Sep 07 '18 at 00:19
  • Way too many possible cause of the is error. For all we know you increment one of the pointers and lose the pointer to the beginning. – user4581301 Sep 07 '18 at 00:19
  • 1
    *I've already asked my TA about this, and she didn't have any idea about how to solve it* -- Needless to say, you're in trouble. -- *I read that every new[] should match with a delete[], and that's what I have* -- If you count in your source code, yes they may match. But it is much more than the number matching. But what if your logic causes `delete[]` to be called more times than `new[]`? Or if you've thrown away the pointer value that `new[]` gave to you, and instead you're deleting a pointer value that didn't match the `new[]` value? – PaulMcKenzie Sep 07 '18 at 00:31
  • *If I say "screw it" and remove all of the delete[]'s from the destructors* -- Rule of 3. Were you taught that you should have copy constructor and assignment operator? – PaulMcKenzie Sep 07 '18 at 00:39
  • @PlinyTheElder I edited the post to provide some relevant code. – Chunky Is Dead Sep 07 '18 at 00:59
  • 1
    You didn't provide a `main` to see how you're using these classes. But even so: `m_showrooms[i] = *new Showroom(d.m_showrooms[i]);` instant memory leak. You also lack assignment operators for `Showroom` and `Dealership`. And honestly, why don't these schools teach how to create your own `vector` class if they insist "you can't use `std::vector`, instead of these one-off assignments where `new` and `delete` are strewn all over the place? – PaulMcKenzie Sep 07 '18 at 01:01
  • 1
    Showroom's constructor `new[]s` an array of `m_vehicles`, then procedes to uselessly `new` a series of vehicles, copying the `new`ed vehicle to its separately `new[]`ed array, then completely leak each `new`ed vehicle (there's your leak). The showroom object has the same exact pattern of improper memory use. Both classes fail to comply with the Rule Of Three. Bottom line: the shown code's dynamic memory allocation is completely wrong in multiple fundamental ways, and all its faults can't be fully explained in just a few paragraphs on stackoverflow.com. Sorry. There are too many problems here. – Sam Varshavchik Sep 07 '18 at 01:03
  • 2
    Please post an [mcve], with an emphasis on minimal. Remove as much code as possible until you get down to the smallest amount of code that reproduces the issue then post that. – xaxxon Sep 07 '18 at 01:05
  • 1
    You haven't added any code for `AddVehicle` or `AddShowroom`, but assuming you are assigning your `Showroom` to those variables in `Dealership`, then you are double-deleting because the variables are local and fall out of scope, so they're deleted, plus you delete the pointers you have. I don't envy you, learning C++ is difficult when you're forced to learn about this kind of nonsense. Once you get through your course, you'll soon realise that you pretty much never write code like this, and tend to just use `std::vector` and suddenly most of these problems just go away. – Tas Sep 07 '18 at 01:10
  • @xaxxon I added a minimal example which still causes the issue. – Chunky Is Dead Sep 07 '18 at 01:17
  • `dealer.AddShowroom(&sh);` -- Where is `AddShowroom`? – PaulMcKenzie Sep 07 '18 at 01:24
  • @PaulMcKenzie Just added it in an edit. – Chunky Is Dead Sep 07 '18 at 01:27
  • You failed to expand the showroom array when you add the second entry -- all you did was increment a counter -- that doesn't expand the array. So you're saying the TA couldn't see this? This is why teaching how to create a vector class properly is more important than throwing `new` and `delete` around in some disorganized fashion. Learning how to create a vector class, you at least get a chance to see how this all works. – PaulMcKenzie Sep 07 '18 at 01:30
  • @PaulMcKenzie What do you mean by I "failed to expand the showroom array"? I did accidentally leave it's max capacity at 1 (my bad, I fixed that now), but even when the array is large enough it still segfaults. I do really wish that we could use vectors, but this lab requires arrays. – Chunky Is Dead Sep 07 '18 at 01:35
  • I mean what I stated -- the array is not expanded if your initial size is too small. Is that not correct? Second, when I mention `vector`, I mean creating your *own* vector class. That teaches you something of value -- throwing new and delete around and hoping something sticks teaches nothing except frustration for new students. – PaulMcKenzie Sep 07 '18 at 01:37
  • Also to mention -- your `main` function is small now, but if you add anything of substance to it, you're right back to where you started from with the double deletion issues. So do you want to address *all* of the issues with your code? I'll do a big favor: [see this example of a home-made vector class](http://coliru.stacked-crooked.com/a/ddb7ba5993b48431). That code covers most of the points you may have missed are or misunderstanding with your assignment and IMO, this is what could have been taught in terms of memory management. – PaulMcKenzie Sep 07 '18 at 01:45
  • Good job on the minimal but still not complete. Your example should be in the form of a c9mplete program that someone could copy paste and compile on their computer. Next time this will help you get the answer you want faster :) – xaxxon Sep 07 '18 at 01:56
  • One thing missed here is the true beauty of the MCVE: Producing one reduces the noise around the bug. The ultimate goal is zero noise, a program that is the bug, the whole bug, and nothing but the bug. But you almost never get there. You get part way and spot and fix the mistake yourself. This is why the comment from Sam Varshavchik is so important: Less code == less noise. Less code makes it easier to isolate where the error is, and an isolated bug is often a solved bug. When you have isolated the bug and still can't solve it, that's when you should ask a question at Stack Overflow. – user4581301 Sep 07 '18 at 04:18

2 Answers2

1

There are many issues with your code, and they could all lead to segfaults in one way or another.

To add to what @ShadowRanger has pointed out, sh and sh2 are not dynamically allocated, and the dealer is keeping a reference to them. These two objects get destroyed automatically when they go out of scope. So when that happens dealer will be operating on objects that no longer exists and a segmentation fault will ensue.

Another cause of segmentation fault I see is due to m_showrooms not getting resized when its capacity is exceeded. The line m_showrooms = new Showroom[m_maxCapacity]; allocates m_maxCapacity number of slots for Showroom pointers. As you call AddShowroom, you need to check if this capacity is going to be exceeded. If so, you need to allocated a larger array, move the objects over, then destroy the old array (and not the objects it houses).

From what I can tell, you seem to be unfamiliar with the idea of how memory is handled in C++. I suggest spending time to figure this out because it will save you a lot of headaches down the road.

lightalchemist
  • 10,031
  • 4
  • 47
  • 55
0

These lines are guaranteed memory leaks:

m_vehicles[i] = *new Vehicle(s.m_vehicles[i]);

and:

m_showrooms[i] = *new Showroom(d.m_showrooms[i]);

That's dynamically allocating, dereferencing, and using the dereferenced value to copy assign into the array. But the pointer to the original dynamically allocated value is immediately lost; you're leaking memory the instant you allocate it.

You really wanted:

m_vehicles[i] = Vehicle(s.m_vehicles[i]);

and:

m_showrooms[i] = Showroom(d.m_showrooms[i]);

Your segfaulting code is faulting for obvious reasons:

Dealership dealer("dealer", 1);

claims at most one showroom will be allocated, then you call AddShowroom twice, and write off the end of the allocated memory (likely corrupting the heap, and who knows what else).

I can't say for sure why it's complaining about unallocated pointers, but you've got so many leaks and out-of-bounds errors lying around, with so much of your code omitted, that it's hard to say for sure I haven't missed something, vs. the code with the error isn't there to check. The sort of error you're experiencing could be caused by heap corruption (which you're definitely causing at various times) or by double delete[]ing a pointer. No obvious code is doing this, but you haven't provided a MCVE (what you've provided is neither minimal nor complete), so it could be hiding.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • Thanks for the feedback. I guess I really need to go back and review the basic concepts behind pointers and such. Although this doesn't really provide me with an answer, I'm accepting it because I don't think it's productive to keep this question open. – Chunky Is Dead Sep 07 '18 at 01:45