当我直接遇到这个问题时,我正在用C ++进行内存管理。当我尝试从Solver类末尾的地图中删除抽象指针时,有时会发生seg-fault。这只发生在一些值上,但不是全部。
我完全按照我所知道的那样使用dbx,并且我发现指针是有效的,但是在失败的删除中,它们指向的似乎是一个vanilla配置,而不是像下面的示例PegConfiguration。调用所有虚拟方法(如toString()甚至isGoal()都会在这些指针上失败,但像stepsToString()这样的非虚方法可以正常工作。
This guy's问题看起来很相似,但我无法理解那里的解决方案。他说他解决了,但没说真的。
Solver.cpp
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include "Solver.h"
#include "Configuration.h"
using namespace std;
deque<Configuration*> Solver::solve(Configuration* initial)
{
paths.insert(pair<string,Configuration*>(initial->toString(), initial));
configs.push_back(initial);
//Continue while there are still configurations
//and the current configuration is not the goal
while(!configs.empty() &&
!configs.front()->isGoal())
{
Configuration* cfg = configs.front();
configs.pop_front();
//if the configuration cannot lead to the solution, throw away
if(!(cfg->isFailure()))
{
vector<Configuration*> newConfigs = (cfg->getNeighbors());
for(int i = 0; i < newConfigs.size(); i++)
{
Configuration* newConfig = newConfigs[i];
//if it is a new config, not in the map
if(paths.insert(pair<string, Configuration*>(newConfig->toString(), cfg)).second)
{
configs.push_back(newConfig);
}
else
{
//delete newConfig;
}
}
}
}//end while
//if there was a solution, work it out
//if there is none, return empty vector
if(!configs.empty())
{
// put goal configuration value in solutions stack
// find previous configuration in map
// put that configuration value in stack
// continue until previous configuration == current configuration
// in other words, reached initial configuration
//send solution stack back to main
//which will handle printing it out
//remove pointers from previous containers,
//so their deletion doesn't screw things up
Configuration* cfg = configs.front();
configs.pop_front();
string key;
do
{
solutions.push_front(cfg);
key = cfg->toString();
cfg = paths[key];
paths[key] = NULL;
} while(!(cfg->toString() == key));
}
//clean up
for(map<string, Configuration*>::iterator iter = paths.begin();
iter != paths.end();
iter++)
{
delete iter->second; //problem occurs HERE
}
for(int i = 0; i < configs.size(); ++i)
{
delete configs[i];
}
paths.clear();
configs.clear();
return solutions;
}//end solve
Configuration.h
#ifndef CONFIGURATION_H
#define CONFIGURATION_H
#include <string>
#include <vector>
class Configuration
{
public:
/**
* Destructor initialized as virtual so that it can be overridden by
* subclasses.
*/
virtual ~Configuration() {}
/**
* Does this configuration match the target value?
*
* @return true if match, false elsewise
*/
virtual bool isGoal() const;
/**
* Can this configuration ever be solved?
*
* @returns true if impossible to solve, false elsewise
*/
virtual bool isFailure() const = 0;
/**
* Basic string representation of configuration. Differs for each class.
*
* @returns string representation of configuration
*/
virtual std::string toString() const = 0;
/**
* Comparation operator for map-sorting. Compares configuration values.
*
* @returns true if value is greater than other's value, false elsewise
*/
bool operator<(const Configuration& other ) const;
/**
* Return all of this config's neighbors (difference of only a single step).
*
& @returns vector of new configurations
*/
virtual std::vector<Configuration*> getNeighbors() = 0;
/**
*
* @returns string representation of valid steps.
*/
std::string stepsToString();
protected:
// contains the steps that are possible for this configuration to reach
// another valid configuration
static std::vector<int> steps;
//the target configuration value
static int _goal;
//the current value of this configuration
int _value;
};//end Configuration
#endif
Configuration.cpp
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "Configuration.h"
using namespace std;
int Configuration::_goal = 1234;
vector<int> Configuration::steps;
//
// Name: isGoal()
//
bool Configuration::isGoal() const
{
return (_value == Configuration::_goal);
}
//
// Name: operator<
//
bool Configuration::operator<(const Configuration& other) const
{
bool answer = false;
if(_value < other._value) { answer = true; }
return answer;
}
//
// Name: stepsToString
//
string Configuration::stepsToString()
{
stringstream ss;
for(int i = 0; i < Configuration::steps.size(); i++)
{
ss << Configuration::steps[i] << " ";
}
return ss.str();
}
PegConfiguration.cpp
#include <vector>
#include <string>
#include <sstream>
#include "PegConfiguration.h"
using namespace std;
vector<char> PegConfiguration::goal;
/**
* Value constructor.
*
* @param value board value of new Configuration
*/
PegConfiguration::PegConfiguration(vector<char> val, int empty):
value(val),
emptyIndex(empty) {}
/**
* Copy constructor.
*
* @param configuration to copy
*/
PegConfiguration::PegConfiguration(const PegConfiguration::PegConfiguration& copy):
value(copy.value),
emptyIndex(copy.emptyIndex) {}
/**
* Constructor for initial puzzle configuration. Given the initial number
* of pegs on one side of the board, it constructs the initial and goal
* value.
*
* @param numPegs number of pegs on one side of board
*/
PegConfiguration::PegConfiguration(int numPegs):
value(2 * numPegs + 1, '.'),
emptyIndex(numPegs)
{
goal = vector<char>(2 * numPegs + 1, '.');
fill(value.begin(), value.begin() + numPegs, 'R');
fill(value.rbegin(), value.rbegin() + numPegs, 'B');
fill(goal.begin(), goal.begin() + numPegs, 'B');
fill(goal.rbegin(), goal.rbegin() + numPegs, 'R');
}
/**
* Goal configuration is an exact mirror of the initial board.
*
* @returns true if this is the goal configuration.
*/
bool PegConfiguration::isGoal() const
{
return (value == goal);
}
/**
* Is this puzzle impossible to solve? Nope.
*
* @returns false always.
*/
bool PegConfiguration::isFailure() const
{
return false;
}
/**
* Basic string representation of configuration value.
*
* @returns string representation of configuration value
*/
std::string PegConfiguration::toString() const
{
stringstream ss;
for(int i = 0; i < value.size(); ++i)
{
ss << value[i] << " ";
}
return ss.str();
}//end toString
/**
* The empty space can either move one space right or left, or jump
* two spaces right or left, in both cases swapping with the peg in that
* location. The only restriction is where the peg is -- if it's too far
* to the left, it can't jump left, for example.
*
* @returns vector of neighbor configuration pointers
*/
std::vector<Configuration*> PegConfiguration::getNeighbors()
{
vector<Configuration*> neighbors;
//jump one to the left
if((emptyIndex - 1) >= 0)
{
vector<char> newValue(value);
(newValue[emptyIndex], newValue[emptyIndex - 1]);
neighbors.push_back(new PegConfiguration(newValue, emptyIndex - 1));
}
//jump two to the left
if((emptyIndex - 2) >= 0)
{
vector<char> newValue(value);
swap(newValue[emptyIndex], newValue[emptyIndex - 2]);
neighbors.push_back(new PegConfiguration(newValue, emptyIndex - 2));
}
//jump one to the right
if((emptyIndex + 1) < value.size())
{
vector<char> newValue(value);
swap(newValue[emptyIndex], newValue[emptyIndex + 1]);
neighbors.push_back(new PegConfiguration(newValue, emptyIndex + 1));
}
//jump two to the right
if((emptyIndex + 2) < value.size())
{
vector<char> newValue(value);
swap(newValue[emptyIndex], newValue[emptyIndex + 2]);
neighbors.push_back(new PegConfiguration(newValue, emptyIndex + 2));
}
return neighbors;
}//end getNeighbors
答案 0 :(得分:1)
你的问题很清楚。这一行:
if(paths.insert(pair<string, Configuration*>(newConfig->toString(), cfg)).second)
在求解器函数中,cfg
指针会多次插入paths
地图。最后,您遍历该地图并delete
其所有元素。如果在地图中多次出现相同的指针,它将被多次删除,因此,您将崩溃。
这也解释了您使用调试器观察到的内容。如果一个类已被删除,它的vtable将被“重新”转换为vtable的基本类版本,即你所指出的“vanilla”配置。
上面给出的这条线看起来很可疑,你确定它是正确的吗?
如果它是正确的,并且cfg
确实应该在paths
地图中多次显示,那么我建议您使用boost::shared_ptr
或等效std::tr1::shared_ptr
来实施正确的参考计数和自动删除你需要的。