I want to improve my C++ skills ( or learn it properly ) so I decided to work on some algorithms for maze generation. Here is the gist:https://gist.github.com/imerovic/0cb7396bb67b345235054b5027d2eadfI have some doubts about memory management and if I'm using pointers properly.Please review it because I wanna know if I'm on the right track with the best practices.Cell.h#pragma once#include <unordered_map>#include <vector>#include <ostream>#include <string>class Cell{public:    Cell(int Row, int Column);    virtual ~Cell();    Cell* Link(Cell* Other, bool bIsBidirectional = true);    Cell* Unlink(Cell* Other, bool bIsBidirectional = true);    std::vector<Cell*> GetLinks();    bool IsLinkedTo(Cell* Other);    std::vector<Cell*> GetNeighbors();    bool IsNeighbor(Cell* Other);    virtual std::unique_ptr<class Distances> GetDistances();    int GetRow() const    { return Row; };    int GetColumn() const    { return Column; };    Cell* North;    Cell* South;    Cell* East;    Cell* West;    friend std::ostream& operator<<(std::ostream& os, const Cell& cell);private:    int Row;    int Column;    std::unordered_map<Cell*, bool> Links;};Cell.cpp#include Cell.h#include Distances.hCell::Cell(int Row, int Column)        : Row(Row),          Column(Column),          North(nullptr),          East(nullptr),          West(nullptr),          South(nullptr){}Cell::~Cell(){}Cell* Cell::Link(Cell* Other, bool bIsBidirectional){    Links.emplace(Other, true);    if (bIsBidirectional)    {        Other->Link(this, false);    }    return this;}Cell* Cell::Unlink(Cell* Other, bool bIsBidirectional){    auto Itr = Links.find(Other);    if (Itr != Links.end())    {        Links.erase(Itr);    }    if (bIsBidirectional)    {        Other->Unlink(this, false);    }    return this;}std::vector<Cell*> Cell::GetLinks(){    std::vector<Cell*> Cells;    for (auto& Itr : Links)    {        Cells.push_back(Itr.first);    }    return Cells;}bool Cell::IsLinkedTo(Cell* Other){    auto Itr = Links.find(Other);    return Itr != Links.end();}std::vector<Cell*> Cell::GetNeighbors(){    std::vector<Cell*> Neighbors;    if (North != nullptr)    {        Neighbors.push_back(North);    }    if (South != nullptr)    {        Neighbors.push_back(South);    }    if (East != nullptr)    {        Neighbors.push_back(East);    }    if (West != nullptr)    {        Neighbors.push_back(West);    }    return Neighbors;}bool Cell::IsNeighbor(Cell* Other){    if (North != nullptr && North == Other)    {        return true;    }    if (East != nullptr && East == Other)    {        return true;    }    if (South != nullptr && South == Other)    {        return true;    }    return West != nullptr && West == Other;}std::unique_ptr<Distances> Cell::GetDistances(){    std::unique_ptr<Distances> Dist(new Distances(this));    //Distances* Dist = new Distances(this);    std::vector<Cell*> Frontier;    Frontier.push_back(this);    while (Frontier.size() > 0)    {        std::vector<Cell*> NewFrontier;        for (auto& Cell : Frontier)        {            auto Links = Cell->GetLinks();            for (auto& Link : Links)            {                if (Dist->Contains(Link))                {                    continue;                }                Dist->SetDistance(Link, Dist->GetDistance(Cell) + 1);                NewFrontier.push_back(Link);            }        }        Frontier.clear();        Frontier = NewFrontier;    }    return Dist;}std::ostream& operator<<(std::ostream& os, const Cell& cell){    os << Cell [ << cell.Row << ,  << cell.Column << ];    return os;}Grid.h#pragma once#include <vector>#include <ostream>class Grid{public:    Grid(int Rows, int Columns);    virtual ~Grid();    std::vector<class Cell*> GetCells();    class Cell* GetCellAt(int Row, int Column);    virtual class Cell* GetRandomCell();    virtual int GetSize() const;    virtual std::string ContentsOf(class Cell* cell) const;    int GetRows() const    { return Rows; }    int GetColumns() const    { return Columns; }    friend std::ostream& operator<<(std::ostream& os, const Grid& grid);    static std::string DistanceToString(int Value, const std::vector<std::string>& BaseChars);protected:    virtual std::vector<std::vector<class Cell*>> PrepareGrid();    virtual void ConfigureCells();private:    int Rows;    int Columns;    std::vector<std::vector<class Cell*>> Cells;};Grid.cpp#include <cstdlib>#include <iostream>#include Grid.h#include Cell.hGrid::Grid(int Rows, int Columns)        : Rows(Rows),          Columns(Columns){    Cells = PrepareGrid();    ConfigureCells();}Grid::~Grid(){    for (auto& RowItr : Cells)    {        for (auto& Cell : RowItr)        {            delete Cell;            Cell = nullptr;        }    }    Cells.empty();}std::vector<std::vector<class Cell*>> Grid::PrepareGrid(){    std::vector<std::vector<Cell*>> vec(Rows, std::vector<Cell*>(Columns));    for (int i = 0; i < Rows; ++i)    {        for (int j = 0; j < Columns; ++j)        {            vec[i][j] = new Cell(i, j);        }    }    return vec;}void Grid::ConfigureCells(){    for (auto& RowItr : Cells)    {        for (auto& Cell : RowItr)        {            int Row = Cell->GetRow();            int Col = Cell->GetColumn();            Cell->North = ((Row - 1) > -1) ? Cells[Row - 1][Col] : nullptr;            Cell->South = ((Row + 1) < Rows) ? Cells[Row + 1][Col] : nullptr;            Cell->West = ((Col - 1) > -1) ? Cells[Row][Col - 1] : nullptr;            Cell->East = ((Col + 1) < Columns) ? Cells[Row][Col + 1] : nullptr;        }    }}std::vector<class Cell*> Grid::GetCells(){    std::vector<Cell*> vec;    for (auto& RowItr : Cells)    {        for (auto& Cell : RowItr)        {            vec.push_back(Cell);        }    }    return vec;}class Cell* Grid::GetCellAt(int Row, int Column){    if (Row < 0 || Row > Rows || Column < 0 || Column > Columns)    {        return nullptr;    }    return Cells[Row][Column];}class Cell* Grid::GetRandomCell(){    int Row = std::rand() % Rows;    int Col = std::rand() % Columns;    std::cout << Getting random cell at:  << Row << ,  << Col << std::endl;    return GetCellAt(Row, Col);}int Grid::GetSize() const{    return Rows * Columns;}std::string Grid::ContentsOf(class Cell* cell) const{    std::string Row = std::to_string(cell->GetRow());    std::string Col = std::to_string(cell->GetColumn());    //return Row + , + Col;    return  ;}std::string Grid::DistanceToString(int Value, const std::vector<std::string>& BaseChars){    std::string res = ;    int TargetBase = BaseChars.size();    do    {        res = BaseChars[Value % TargetBase] + res;        Value = Value / TargetBase;    }    while (Value > 0);    return res;}std::ostream& operator<<(std::ostream& os, const Grid& grid){    std::string str = +;    for (int i = 0; i < grid.Columns; ++i) {        str += ---+;    }    str += \n;    for (int i = 0; i < grid.Rows; ++i) {        std::string Top = |;        std::string Bottom = +;        for (int j = 0; j < grid.Columns; ++j) {            Cell* cell = grid.Cells[i][j];            std::string Body =   + grid.ContentsOf(cell) +  ;            std::string EastBoundary = cell->IsLinkedTo(cell->East) ?   : |;            Top += Body + EastBoundary;            std::string SouthBoundary = cell->IsLinkedTo(cell->South) ?     : ---;            Bottom += SouthBoundary + +;        }        str += Top + \n;        str += Bottom + \n;    }    os << str;    return os;}
Working on some maze generation algorithms
c++;object oriented