【发布时间】:2021-08-14 19:31:10
【问题描述】:
我在 C++ 中有一个 A* 搜索的实现,它在其他方面工作正常,但存在内存泄漏。我尝试使用 CRT 库来帮助查找泄漏,似乎我创建的 SearchState 对象没有正确释放,但我不太明白为什么。
编辑:我包含了完整的 solitaire.h 以便可以编译代码。
EDIT2:抱歉,忘记包含测试数据。
司机
#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
搜索.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
纸牌.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
【问题讨论】:
-
使用智能指针,减少或消除
new的使用。否则,我们需要查看完整的代码,以了解何时、何地以及如何分配和释放对象。有一个逻辑流程需要研究——这不仅仅是将每个对new的调用与delete匹配的问题。 -
如果您需要帮助找出如此大的程序中的问题,请编译该程序。目前是不可能的。您可以可能将其缩短为minimal reproducible example。
-
没有测试数据。没有它,程序就无法运行。
标签: c++ memory-leaks polymorphism dynamic-memory-allocation a-star