C ++:抽象指针的映射似乎正在切换到抽象类

时间:2011-02-17 04:03:15

标签: c++ memory-management segmentation-fault

当我直接遇到这个问题时,我正在用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

1 个答案:

答案 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来实施正确的参考计数和自动删除你需要的。