Having trouble finding the source of a memory leak in C++ code

I have in C++ an implementation of an A* search, which is otherwise working fine, but has a memory leak. I’ve tried to use the CRT library to help find the leak and it seems that the SearchState objects I create are not deallocated properly, but I can’t quite figure out why.

EDIT: I included the full solitaire.h so that the code can be compiled.

EDIT2: Sorry, forgot to include the test data.

driver

#include "astar.h"
#include "solitaire.h"

int main()
{
    std::ifstream in("instance.txt");
    Solitaire s(in);
    AStar solver;
    solver.solve(&s);
    system("pause");
}

astar.h

#ifndef ASTAR_H
#define ASTAR_H

#include "search.h"
#include <map>
#include <iostream>



struct AStar : public SearchAlg {

    // Nodes for the priority queue
    struct Node {
    public:
        Node();
        Node(const Node& n);
        Node(SearchState* _s, int _f);
        ~Node();
        Node& operator=(Node other) {
            this->state = other.state->clone();
            this->f = other.f;
            return *this;
        }
        SearchState* state = nullptr;
        int f = 0;
    };

    // Comparator for the priority queue
    struct Compare {
        bool operator()(Node lhs, Node rhs) const {
            if (lhs.f > rhs.f) return true;
            if (lhs.f == rhs.f) return (lhs.state->g < rhs.state->g);
            return false;
        }
    };

    // Tuple for State Table data 
    struct StateData {
        StateData() {};
        StateData(int _g, std::string _p, std::string _a);
        int g = 0;
        std::string parent = "";
        std::string action = "";
    };

public:
    void solve(SearchSpace* dom);
    int cost = INT_MAX;
};

AStar::Node::Node()
{
    state = nullptr;
    f = 0;
}

AStar::Node::Node(const Node& n)
{
    state = n.state->clone();
    f = n.f;
}

AStar::Node::Node(SearchState* _s, int _f)
{
    state = _s;
    f = _f;
}

AStar::Node::~Node()
{
    if (state != nullptr) delete state;
}

AStar::StateData::StateData(int _g, std::string _p, std::string _a)
{
    g = _g;
    parent = _p;
    action = _a;
}

void AStar::solve(SearchSpace* dom)
{
    std::priority_queue<Node, std::vector<Node>, Compare> fringe;
    std::map<std::string, StateData> StateTable;

    Node begin(dom->init->clone(), dom->init->h());

    begin.state->print();

    std::string initHash = (begin.state)->hash();
    StateData initData(begin.state->g, "", "");
    StateTable[initHash] = initData;

    fringe.push(begin);

    while (!fringe.empty())
    {
        Node n = fringe.top();
        fringe.pop();
        SearchState* s = n.state;

        std::string hash = s->hash();

        if (StateTable.count(hash) == 1 && s->g > StateTable[hash].g)
            continue;

        if (s->goal())
        {
            std::cout << "Found a solution! (Cost: " << s->g << ")" << std::endl;
            std::string path = "";
            StateData data = StateTable[hash];
            std::string act = data.action;

            while (act != "")
            {
                path = act + " " + path;
                //std::cout << "Current path: " << path << std::endl;
                hash = data.parent;
                data = StateTable[hash];
                act = data.action;
            }
            std::cout << path << std::endl;
            cost = s->g;
            return;
        }

        std::queue<std::pair<std::string, SearchState*>> kids = s->expand();
        while (!kids.empty())
        {

            std::pair<std::string, SearchState*> p = kids.front();
            kids.pop();
            std::string a = p.first;
            SearchState* k = p.second;

            std::string kHash = k->hash();
            bool found = (StateTable.count(kHash) == 1);

            if (!found || StateTable[kHash].g > k->g)
            {
                StateData newData(k->g, hash, a);
                StateTable[kHash] = newData;
                Node newNode(k->clone(), k->g + k->h());
                fringe.push(newNode);
            }
            delete k;
        }
    }

    std::cout << "No solution found!" << std::endl;

}

#endif ASTAR_H

search.h

#ifndef SEARCH_H
#define SEARCH_H

#include <string>
#include <queue>
#include <fstream>

struct SearchState {
public:
    SearchState() {};
    SearchState(SearchState& s) { g = s.g; };
    virtual ~SearchState() {};
    virtual SearchState* clone() = 0;
    virtual void print() = 0;

    virtual int h() = 0;
    virtual bool goal() = 0;
    virtual std::string hash() = 0;
    virtual std::queue<std::pair<std::string, SearchState*>> expand() = 0;

    int g = 0;
};

struct SearchSpace {
public:
    SearchSpace() {};
    SearchSpace(std::ifstream in) {};
    ~SearchSpace();
    SearchState* init = nullptr;
};

SearchSpace::~SearchSpace()
{
    if (init != nullptr) delete init;
}

struct SearchAlg {
    virtual void solve(SearchSpace* dom) = 0;
};

#endif SEARCH_H

solitaire.h

#ifndef SOLITAIRE_H
#define SOLITAIRE_H

#include <iostream>
#include <stack>

struct Solitaire : public SearchSpace {

    enum {
        Npos = 13,
        Nrank = 13,
        Nsuit = 4,
        Ncards = Nrank * Nsuit,
        Ndeck = 24,
        Nfound = 4,
        Ntab = 7,

        pDeck = 0,
        pPile = 1,
        pFound = 2,
        pTab = 6
    };

    static const int DISTANCE[Npos][Npos];
    static const char SUITS[Nsuit];
    static const char RANKS[Nrank];

    struct State : public SearchState
    {
    public:
        State(std::ifstream& in);
        State(State& s);
        ~State() {};
        SearchState* clone();
        void print();
    private:
        int pos = 0;
        std::stack<int> Deck;
        std::stack<int> Pile;
        std::vector<std::stack<int>> Found;
        std::vector<std::stack<int>> TabV;
        std::vector<std::stack<int>> TabH;

        void restack();
        void drawCard();
        void pileToFoundation(int f1);
        void pileToTableau(int t1);
        void tabToFoundation(int t1, int f1);
        void tabToTab(int t1, int t2, int num);
        int moveNum(int t1, int t2);

        int h();
        bool goal();
        std::string hash();
        std::queue<std::pair<std::string, SearchState*>> expand();
    };

public:
    Solitaire() {};
    Solitaire(SearchState* s);
    Solitaire(std::ifstream& in);
private:
    static std::string cardName(int c);
};

const int Solitaire::DISTANCE[][Npos] = {
    { 0, 1, 3, 5, 7, 9, 1, 1, 3, 3, 5, 7, 9},
    { 1, 0, 1, 3, 5, 7, 1, 1, 3, 1, 3, 5, 7},
    { 3, 1, 0, 1, 3, 5, 3, 1, 3, 1, 1, 3, 5},
    { 5, 3, 1, 0, 1, 3, 5, 3, 3, 1, 1, 1, 3},
    { 7, 5, 3, 1, 0, 1, 7, 5, 5, 3, 1, 1, 1},
    { 9, 7, 5, 3, 1, 0, 9, 7, 7, 5, 3, 1, 1},
    { 1, 1, 3, 5, 7, 9, 0, 1, 3, 3, 5, 7, 9},
    { 1, 1, 3, 5, 7, 9, 1, 0, 1, 3, 5, 7, 9},
    { 3, 1, 1, 3, 5, 7, 3, 1, 0, 1, 3, 5, 7},
    { 3, 1, 1, 1, 3, 5, 3, 3, 1, 0, 1, 3, 5},
    { 5, 3, 1, 1, 1, 3, 5, 3, 3, 1, 0, 1, 3},
    { 7, 5, 3, 1, 1, 1, 7, 5, 5, 3, 1, 0, 1},
    { 9, 7, 5, 3, 1, 1, 9, 7, 7, 5, 3, 1, 0},
};

const char Solitaire::SUITS[] = { 'S','H','C','D' };
const char Solitaire::RANKS[] = { 'A','2','3','4','5','6','7','8','9','T','J','Q','K' };

Solitaire::Solitaire(std::ifstream& in)
{
    if (init != nullptr) delete init;
    std::cout << "solitaire new" << std::endl;
    init = new State(in);
    std::cout << init << std::endl;

}

Solitaire::Solitaire(SearchState* s)
{
    State* s2 = (State*)s;
    init = new State(*s2);
}

std::string Solitaire::cardName(int c) {
    std::string s = "";
    int suit = c / Nrank;
    int rank = c % Nrank;
    s += RANKS[rank];
    s += SUITS[suit];
    return s;
}

Solitaire::State::State(std::ifstream& in) {
    std::cout << "State(ifstream in)" << std::endl;
    std::cout << "This: " << this << std::endl;
    int i, j;
    int c;

    Found.resize(Nfound);
    TabV.resize(Ntab);
    TabH.resize(Ntab);

    for (i = 0; i < Ndeck; i++) {
        in >> c;
        Deck.push(c);
    }
    for (i = 0; i < Ntab; i++) {
        in >> c;
        TabV[i].push(c);
        for (j = i; j > 0; j--) {
            in >> c;
            TabH[i].push(c);
        }
    }
    pos = 0;

    g = 0;
}

Solitaire::State::State(State& s) : SearchState(s) {
    pos = s.pos;
    Deck = s.Deck;
    Pile = s.Pile;
    Found = s.Found;
    TabV = s.TabV;
    TabH = s.TabH;
    g = s.g;
}

SearchState* Solitaire::State::clone() {
    return new State(*this);
}

void Solitaire::State::print() {
    std::cout << "Pos: " << pos << "n";

    if (!Deck.empty())
    {
        std::cout << "Deck:n";
        std::stack<int> temp = Deck;
        while (!temp.empty())
        {
            std::cout << cardName(temp.top()) << " ";
            temp.pop();
        }
        std::cout << "n";
    }

    if (!Pile.empty())
    {
        std::cout << "Pile:n";
        std::stack<int> temp = Pile;
        while (!temp.empty())
        {
            std::cout << cardName(temp.top()) << " ";
            temp.pop();
        }
        std::cout << "n";
    }

    for (int i = 0; i < Nfound; i++)
        if (!Found[i].empty())
        {
            std::cout << "F" << i << ": ";
            std::stack<int> temp = Found[i];
            while (!temp.empty())
            {
                std::cout << cardName(temp.top()) << " ";
                temp.pop();
            }
            std::cout << "n";
        }

    for (int i = 0; i < Ntab; i++)
    {
        if (!TabV[i].empty())
        {
            std::cout << "T" << i << ": ";
            std::stack<int> temp = TabV[i];
            while (!temp.empty())
            {
                std::cout << " " << cardName(temp.top()) << "  ";
                temp.pop();
            }
            temp = TabH[i];
            while (!temp.empty())
            {
                std::cout << "(" << cardName(temp.top()) << ") ";
                temp.pop();
            }
            std::cout << "n";
        }
    }
}

int Solitaire::State::h()
{
    
    int total = (52 - (Deck.size() + Pile.size()) * 20);
    total += 7 * ceil(Deck.size() / 3.0);
    total += 20 * (Deck.size() + Pile.size());

    for (int i = 0; i < Nfound; i++)
        total -= 20 * Found[i].size();

    return total;
}

bool Solitaire::State::goal()
{
    if (Deck.size() + Pile.size() > 0) return false;
    for (int i = 0; i < Ntab; i++)
        if (!TabH[i].empty()) return false;
    return true;
}

std::string Solitaire::State::hash()
{
    std::string s = "p";
    s += std::to_string(pos);
    s += "d";
    std::stack<int> temp;
    temp = Deck;
    int c;
    while (!temp.empty()) {
        c = temp.top();
        temp.pop();
        s += cardName(c);
    }
    s += "p";
    temp = Pile;
    while (!temp.empty()) {
        c = temp.top();
        temp.pop();
        s += cardName(c);
    }
    for (int i = 0; i < Nfound; i++)
    {
        temp = Found[i];
        s += "f";
        while (!temp.empty()) {
            c = temp.top();
            temp.pop();
            s += cardName(c);
        }
    }
    for (int i = 0; i < Ntab; i++)
    {
        temp = TabV[i];
        s += "v";
        while (!temp.empty()) {
            c = temp.top();
            temp.pop();
            s += cardName(c);
        }
        temp = TabH[i];
        s += "h";
        while (!temp.empty()) {
            c = temp.top();
            temp.pop();
            s += cardName(c);
        }
    }

    return s;
}

int Solitaire::State::moveNum(int t1, int t2) {
    int targetCard = TabV[t2].top();
    int numTotal = TabV[t1].size();
    int counter = 0;
    std::stack<int> temp = TabV[t1];
    while (!temp.empty()) {
        int curCard = temp.top();
        temp.pop();
        counter++;
        if ((curCard / 13) % 2 != (targetCard / 13) % 2 && curCard % 13 == targetCard % 13 - 1) {
            if (counter == numTotal) return numTotal;
            return 0;
        }
    }
    return 0;
}

void Solitaire::State::drawCard() {
    g += DISTANCE[pos][pDeck];
    int totalSize = Deck.size() + Pile.size();
    switch (totalSize) {
    case 1:
        g += 7; break;
    case 2:
        g += 8; break;
    default:
        g += 9; break;
    }
    for (int i = 0; i < 3; i++) {
        int c = Deck.top();
        Deck.pop();
        Pile.push(c);
        if (Deck.empty())
            break;
    }
    pos = pPile;
}

void Solitaire::State::restack() {
    g += DISTANCE[pos][pDeck];
    g += 7;
    while (Pile.size() > 0) {
        int c = Pile.top();
        Pile.pop();
        Deck.push(c);
    }
    pos = pPile;
}

void Solitaire::State::pileToFoundation(int f1) {
    g += DISTANCE[pos][pPile] + DISTANCE[pPile][pFound + f1];

    int c = Pile.top();
    Pile.pop();
    Found[f1].push(c);

    g += 20;
    if (Pile.size() >= 3)
        g += 4;

    pos = pFound + f1;
}

void Solitaire::State::pileToTableau(int t1) {
    g += DISTANCE[pos][pPile] + DISTANCE[pPile][pTab + t1];

    int c = Pile.top();
    Pile.pop();
    TabV[t1].push(c);

    g += 20;
    if (Pile.size() >= 3)
        g += 4;

    pos = pTab + t1;
}

void Solitaire::State::tabToFoundation(int t1, int f1) {
    g += DISTANCE[pos][pTab + t1] + DISTANCE[pTab + t1][pFound + f1];

    int c = TabV[t1].top();
    TabV[t1].pop();
    Found[f1].push(c);
    // Reveal hidden card
    if (TabV[t1].empty() && !TabH[t1].empty()) {
        int next = TabH[t1].top();
        TabH[t1].pop();
        TabV[t1].push(next);
    }

    g += 20;
    if (Found[f1].size() > 1)
        g += 4;

    pos = pFound + f1;
}

void Solitaire::State::tabToTab(int t1, int t2, int num) {
    g += DISTANCE[pos][pTab + t1] + DISTANCE[pTab + t1][pTab + t2];

    std::stack<int> temp;
    for (int i = 0; i < num; i++)
    {
        int c = TabV[t1].top();
        TabV[t1].pop();
        temp.push(c);
    }

    for (int i = 0; i < num; i++)
    {
        int c = temp.top();
        temp.pop();
        TabV[t2].push(c);
    }
    //Reveal hidden card
    if (TabV[t1].size() == 0 && TabH[t1].size() > 0)
    {
        int next = TabH[t1].top();
        TabH[t1].pop();
        TabV[t1].push(next);
    }

    g += 11 * (num + 1);

    pos = pTab + t2;
}

std::queue<std::pair<std::string, SearchState*>> Solitaire::State::expand()
{

    if (g == 0)
    {
        drawCard();
        drawCard();
        pileToTableau(3);
        tabToFoundation(4, 2);
        tabToTab(4, 0, 1);
        tabToTab(1, 0, 1);
        drawCard();
        pileToTableau(3);
        pileToTableau(3);
        tabToTab(5, 3, 1);
        tabToTab(2, 1, 1);
        drawCard();
        drawCard();
        pileToTableau(5);
        tabToTab(3, 5, 5);
        tabToTab(3, 2, 1);
        pileToTableau(2);
        drawCard();
        drawCard();
        pileToFoundation(0);
        tabToFoundation(3, 3);
        tabToFoundation(6, 0);
        drawCard();
        pileToTableau(0);
        tabToTab(3, 0, 1);
        pileToTableau(0);
        tabToTab(2, 3, 3);
        pileToTableau(3);
        restack();
        drawCard();
        pileToTableau(0);
        drawCard();
        pileToFoundation(0);
        tabToTab(4, 6, 1);
        tabToFoundation(4, 0);
        pileToFoundation(0);
        drawCard();
        drawCard();
        pileToTableau(1);
        tabToFoundation(5, 0);
        tabToTab(4, 1, 1);
        pileToTableau(4);
        tabToTab(5, 4, 6);
        tabToTab(5, 1, 1);
        tabToTab(5, 2, 1);
        tabToTab(5, 3, 1);
        pileToTableau(3);
        tabToFoundation(5, 1);
        pileToFoundation(1);
        tabToTab(6, 5, 2);
        tabToFoundation(6, 1);
        tabToTab(6, 5, 1);
        tabToFoundation(6, 2);
        tabToFoundation(6, 1);
        pileToFoundation(1);
        pileToTableau(2);
        pileToTableau(4);
        pileToTableau(3);

        std::cout << "After starting moves:" << std::endl;
        print();
        std::cout << g << std::endl;
    }
    

    std::queue<std::pair<std::string, SearchState*>> q;

    // Action : Draw a card from the deck
    if (Deck.size() + Pile.size() > 0) {
        State* newState = new State(*this);
        if (newState->Deck.empty())
        {
            newState->restack();
            q.push(std::pair<std::string, SearchState*>("RESTACK", newState));
//          std::cout << "kid found: RESTACK" << std::endl;
        }
        else
        {

            newState->drawCard();
            q.push(std::pair<std::string, SearchState*>("DRAW", newState));
//          std::cout << "kid found: DRAW" << std::endl;
        }
    }

    if (Pile.size() > 0) {
        int topPile = Pile.top();

        // Action : Move a card from the pile to the foundation
        for (int i = 0; i < Nfound; i++) {
            if (Found[i].empty() && topPile % 13 == 0) {
                // Moving Ace from Pile to foundation
                State* newState = new State(*this);
                newState->pileToFoundation(i);
                q.push(std::pair<std::string, SearchState*>("P->F" + std::to_string(i), newState));
//              std::cout << "kid found: P->F" + std::to_string(i) << std::endl;
            }
            else if (Found[i].size() > 0) {
                int topFnd = Found[i].top();
                if (topPile / 13 == topFnd / 13 && topPile % 13 == topFnd % 13 + 1) {
                    // Moving other card from Pile to foundation
                    State* newState = new State(*this);
                    newState->pileToFoundation(i);
                    q.push(std::pair<std::string, SearchState*>("P->F" + std::to_string(i), newState));
//                  std::cout << "kid found: P->F" + std::to_string(i) << std::endl;
                }
            }
        }

        // Action : Move a card from the pile to a tableau
        for (int i = 0; i < Ntab; i++) {
            if (TabV[i].empty() && topPile % 13 == 12) {
                // Moving King from pile to empty tableau
                State* newState = new State(*this);
                newState->pileToTableau(i);
                q.push(std::pair<std::string, SearchState*>("P->T" + std::to_string(i), newState));
//              std::cout << "kid found: P->T" + std::to_string(i) << std::endl;
            }
            else if (!TabV[i].empty()) {
                int topTab = TabV[i].top();
                if ((topPile / 13) % 2 != (topTab / 13) % 2 && topPile % 13 == topTab % 13 - 1) {
                    // Moving other card from Pile to tableau
                    State* newState = new State(*this);
                    newState->pileToTableau(i);
                    q.push(std::pair<std::string, SearchState*>("P->T" + std::to_string(i), newState));
//                  std::cout << "kid found: P->T" + std::to_string(i) << std::endl;
                }
            }
        }
    }

    for (int i = 0; i < Ntab; i++)
    {
        if (!TabV[i].empty())
        {
            int topTab = TabV[i].top();

            // Action: Move a card from a tableau to the foundation
            for (int j = 0; j < Nfound; j++) {
                if (Found[j].empty() && topTab % 13 == 0) {
                    //Moving Ace from Tableau to Foundation
                    State* newState = new State(*this);
                    newState->tabToFoundation(i, j);
                    q.push(std::pair<std::string, SearchState*>("T" + std::to_string(i) + "->F" + std::to_string(j), newState));
//                  std::cout << "kid found: T" + std::to_string(i) + "->F" + std::to_string(j) + std::to_string(newState->g - g) + ")" << std::endl;
                }
                else if (!Found[j].empty()) {
                    int topFnd = Found[j].top();
                    if (topTab / 13 == topFnd / 13 && topTab % 13 == topFnd % 13 + 1) {
                        // Moving other card from Tableau to Foundation
                        State* newState = new State(*this);
                        newState->tabToFoundation(i, j);
                        q.push(std::pair<std::string, SearchState*>("T" + std::to_string(i) + "->F" + std::to_string(j), newState));
//                      std::cout << "kid found: T" + std::to_string(i) + "->F" + std::to_string(j) + std::to_string(newState->g - g) + ")" << std::endl;
                    }
                }
            }

            // Action: Move a card from a tableau to a tableau
            for (int j = 0; j < Ntab; j++) {
                if (i != j) {
                    if (TabV[j].empty() && topTab % 13 == 12) {
                        // Moving single king from Tableau to empty Tableau
                        State* newState = new State(*this);
                        newState->tabToTab(i, j, 1);
                        q.push(std::pair<std::string, SearchState*>("T" + std::to_string(i) + "->T" + std::to_string(j), newState));
//                      std::cout << "kid found: T" + std::to_string(i) + "->T" + std::to_string(j) + std::to_string(newState->g - g) + ")" << std::endl;
                    }
                    else if (TabV[j].empty())
                    {
                        std::stack<int> temp = TabV[i];
                        while (temp.size() > 1)
                        {
                            temp.pop();
                        }
                        int c = temp.top();
                        if (c % 13 == 12)
                        {
                            State* newState = new State(*this);
                            newState->tabToTab(i, j, TabV[i].size());
                            q.push(std::pair<std::string, SearchState*>("T" + std::to_string(i) + "->T" + std::to_string(j), newState));
//                          std::cout << "kid found: T" + std::to_string(i) + "->T" + std::to_string(j) + std::to_string(newState->g - g) + ")" << std::endl;
                        }
                    }
                    else if (!TabV[j].empty()) {
                        int num = moveNum(i, j);
                        if (num > 0) {
                            State* newState = new State(*this);
                            newState->tabToTab(i, j, num);
                            q.push(std::pair<std::string, SearchState*>("T" + std::to_string(i) + "->T" + std::to_string(j), newState));
//                          std::cout << "kid found: T" + std::to_string(i) + "->T" + std::to_string(j) + std::to_string(newState->g - g) + ")" << std::endl;
                        }
                    }
                }
            }
        }
    }
    //std::cout << " -------------------------------------- " << std::endl;
    return q;
}

#endif SOLITAIRE_H

instance.txt

4 2 35 26 34 22 50 46 25 23 49 33 14 17 41 7 19 28 47 30 5 40 45 36
20 
44 48
8 43 51 
9 42 39 37 
0 6 29 24 32
31 13 21 3 18 11
27 38 16 1 10 15 12

Source: Windows Questions C++

LEAVE A COMMENT