Question

I'm facing a strange issue. I wrote a Parent abstract class (implementing a pure virtual test() method) and its Child class (implementing the test() method).

class Parent
{
    public :
        Parent();
        virtual ~Parent() = default;

        virtual bool test() const = 0;
};

class Child : public Parent
{
    public :
        bool test() const;
};

Then, I wrote a "Grid" class which is supposed to contain a two dimension array of pointers to Parent. The array is done using the vector library : "_cells" is a width*height vector of pointers to Parent. _cells is filled during Grid object construction using dynamic allocation and freed in the destructor. Operator() (int a, int b) is overloaded in order to be able to call the Parent object using this pattern : myGrid(x,y).

class Grid
{
        int _w, _h;
        std::vector<Parent*> _cells;

    public :
        Grid(int w = 0, int h = 0);
        ~Grid();
        Parent* &operator()(int x, int y);

    private :
        void generate();
};

In my main function, g is a first 2x2 grid created on the stack. Then, it is supposed to destroy g and to construct a new 4x4 grid in g. But it completely fails :

Grid g(2, 2);
std::cout << g(1,1)->test() << std::endl; // Works perfectly
g = Grid(4, 4); // Probably wrong, but don't throw an exception
std::cout << g(1,1)->test() << std::endl; // SIGSEGV

I think that the issue comes from dynamic allocation/desallocation of each cell, but I didn't find a way to solve it.

Here is my full code, I didn't succeeded in simplifying it more. I did my best. Sorry.

#include <iostream>
#include <cstdlib>
#include <vector>

class Parent
{
    public :
        Parent();
        virtual ~Parent() = default;

        virtual bool test() const = 0;
};

Parent::Parent()
{}

class Child : public Parent
{

    public :
        bool test() const;
};

bool Child::test() const
{
    return true;
}

class Grid
{
        int _w, _h;
        std::vector<Parent*> _cells;

    public :
        Grid(int w = 0, int h = 0);
        ~Grid();
        Parent* &operator()(int x, int y);

    private :
        void generate();
};

Grid::Grid(int w, int h) : _w(w), _h(h), _cells(w*h)
{
    generate();
}

Grid::~Grid()
{
    for (auto cell : _cells)
        delete cell;
}

Parent* &Grid::operator()(int x, int y)
{
    return _cells[x*_w+y];
}

void Grid::generate()
{
    int cell_num;
    for (cell_num = 0; cell_num < static_cast<int>(_cells.size()); cell_num++)
        _cells[cell_num] = new Child();
}

int main()
{
    Grid g(2, 2);
    std::cout << g(1,1)->test() << std::endl;
    g = Grid(4, 4);
    std::cout << g(1,1)->test() << std::endl;

    return 0;
}

Thanks.

Was it helpful?

Solution

The Grid class doesn't have a copy-assignment operator, so the compilers default generated version will be used instead. It's very simple, and does only a shallow copy of the members. That means that the pointers created for the temporary object Grid(4, 4) are copied (just the pointers, and not what they point to), and when the temporary object is destroyed so are the pointers (in the destructor for the temporary object). This leaves you with an object g with pointers to now deleted memory.

I suggest you read about the rule of three.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top