C ++:如何“存储”节点

时间:2013-07-25 00:34:25

标签: c++ pointers linked-list nodes

所以我试图从一个简单的迷宫游戏的文本文件中读取以下格式的数据:

A * B * *
B * C * A
C G D * B
D * * * C
E I F * *
F J G * E
G * H C F
H * * * G
I * * E *
J * K F *
K * L * J
L * * * K

第一列是当前节点。第二列,即北方的节点。第三,到其直接的东部,第四,到紧邻的南部,第五,到最近的西部。任何*表示没有节点,指针应该设置为空。

请记住,我是OOP的新秀,我很好奇如何去拯救"将此信息转换为一个名为Node的类。

我有以下课程定义:

public:
    int BuildGraph();
    char name;
    char north;
    char east;
    char south;
    char west;
};  
    std::vector<Node> nodes;

使用代码build >> node.name >> node.north >> node.east >> node.south >> node.west;读入正确的字符。

这正确地获取了我想要的角色。但是,一旦我到达行尾,如何存储此节点然后转到下一行并存储THAT节点?现在,我已经有一个do / while循环遍历它直到它到达文件末尾但当然它每次都会覆盖所有内容所以它有效读入的唯一代码是最后一行L * * * K.如何将第一行保存为节点A,第二行保存为节点B等?我如何在程序的其他地方引用它?现在,我只是创建一个Node类型的对象节点。当我到达行尾时,我需要将节点中的所有内容复制到一个名为A的新节点

编辑:抱歉格式化; Stack Overflow没有将我的4个空格识别为代码缩进。

5 个答案:

答案 0 :(得分:1)

我不确定我是否得到了您想要的内容,但如果您想使用不同的标识符保存Node类型的每个元素,您可以尝试下一个:

pair<char, Node> node_with_name;

然后您可以将名称分配给执行build >> node_with_name.first;的第一个元素 ,然后将其余元素放在节点中,并以相同的方式将其分配给pair的第二个位置。

还应该更改矢量以使用此解决方案:

std::vector<pair<char, Node> > nodes;

最后你会这样做:

nodes.push_back(node_with_name);

在循环的每次迭代中。


修改:我认为map可能更符合您的需求。我会告诉你一个例子:

std::map<char,Node> mymap;

do {
  char name;                          // to store temporarily each node's name
  build >> name;            
  build >> mymap[name].north;      // we start getting the data for the node with this name
  build >> mymap[name].east;
  build >> mymap[name].south;
  build >> mymap[name].west;
}while(! build.eof());

然后您可以输出每个节点的数据:

std::cout << "mymap['A'].north is " << mymap['A'].north << '\n';

地图参考:http://www.cplusplus.com/reference/map/map/operator%5B%5D/

配对参考:http://www.cplusplus.com/reference/utility/pair/

答案 1 :(得分:0)

在文件上使用getline()函数,逐行读取文件并将节点信息解析到节点结构中。

答案 2 :(得分:0)

对你而言,有一件事情是你无法直接创建节点,因为在早期你需要获得指向尚未创建的节点的指针。解决这个问题的一种方法是你似乎在做的事情:存储节点字母而不是指针:

#include <iostream>
class Maze; // Holds all the nodes; defined later.

class Node
{public:
    static const char kNoLink = '*';
    Node(): north(kNoLink), east(kNoLink), south(kNoLink), west(kNoLink) {}
private:
    char name;
    char north;
    char east;
    char south;
    char west;
    friend std::istream& operator>>(std::istream&, Node&);
    friend std::ostream& operator<<(std::ostream&, const Node&);
    friend class Maze;
};

std::istream& operator>>(std::istream& is, Node& node)
{
    return is >> node.name >> node.north >> node.east >> node.south >> node.west; 
}

std::ostream& operator<<(std::ostream& os, const Node& node)
{
    return os << node.name << ' ' << node.north << ' ' << node.east << ' ' 
        << node.south << ' ' << node.west << '\n'; 
}

然后Maze类将它们全部包含在内:

#include <map>

class Maze
{public:
    Maze() {}
    ~Maze();
    Node* GetNode(char name)
    {
        NodeMap::iterator ni = nodeMap.find(name);
        return ni == nodeMap.end()? 0: *ni;
    }
    Node* GoNorth(Node* start) { return GetNode(start->north); }
    Node* GoEast(Node* start) { return GetNode(start->east); }
    Node* GoSouth(Node* start) { return GetNode(start->south); }
    Node* GoWest(Node* start) { return GetNode(start->west); }
private:
    typedef std::map<char, Node*> NodeMap;
    NodeMap nodeMap;
    friend std::istream& operator>>(std::istream&, Maze&);
    friend std::ostream& operator<<(std::ostream&, const Maze&);
};

Maze::~Maze()
{
    // While the map itself will get properly destroyed, its contents won't.
    // So we have to delete the nodes in the map ourselves.
    // For std::map, i->first is the key and i->second is the value.
    for(NodeMap::iterator i = nodeMap.begin(); i != nodeMap.end(); ++i)
        delete i->second;
}

std::istream& operator>>(std::istream& is, Maze& maze)
{
    while(is)
    {
        Node* newNode = new Node;
        is >> newNode;
        maze.nodeMap[newNode.name] = newNode;
    }
    return is;
}

std::ostream& operator<<(std::ostream& os, const Maze& maze)
{
    for(NodeMap::const_iterator i = nodeMap.begin(); i != nodeMap.end(); ++i)
        os << *(i->second);
    return os;
}

现在阅读迷宫只是:

Maze maze;
build >> maze;

打印它就像是:

cout << maze;

这里可以做很多改进,例如直接在节点中存储指针(这需要一个双通道阅读器:你仍然需要存储字母,然后在阅读整个迷宫后链接所有节点)并删除节点和节点映射上的名称,并将所有代码放入Node类(允许您从任何节点管理整个迷宫;读取迷宫后丢弃节点名称,打印出来时重新生成)。

另外,我假设你没有使用C ++ 11。如果是,那么您可以进一步简化代码。例如,这个:

    for(NodeMap::const_iterator i = nodeMap.begin(); i != nodeMap.end(); ++i)
        os << *(i->second);

变为:

    for(auto i: nodeMap)
        os << *(i->second);

总而言之,有比istream更强大的解析选项。查看Boost :: Spirit,如果你认为编写Backus-Naur Form(BNF)并不是什么大问题,并希望看到你可以用C ++中的运算符重载来做的疯狂事情。

答案 3 :(得分:0)

#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>

struct node
{
    char name;
    char north;
    char east;
    char south;
    char west;
}; 

std::istream& operator >> (std::istream& is, node& n)
{
    is >> n.name >> n.north >> n.east >> n.south >> n.west;
    return is;
}

std::ostream& operator << (std::ostream& os, const node& n)
{
    os << n.name << ' ' << n.north << ' ' << n.east << ' ' << n.south << ' ' << n.west;
    return os;
}

int main(int argc, char* argv[])
{
    std::vector<node> nodes;

    node n;

    while(std::cin >> n)
      nodes.push_back(n);

    std::copy(nodes.begin(), nodes.end(), std::ostream_iterator<node>(std::cout,"\n"));
    return 0;
}

$ cat nodes.txt | ./a.out 
A * B * *
B * C * A
C G D * B
D * * * C
E I F * *
F J G * E
G * H C F
H * * * G
I * * E *
J * K F *
K * L * J
L * * * K

答案 4 :(得分:0)

据我所知,您希望能够从文件中读取节点描述并将其存储在内存中的数据结构中。看起来像文字冒险地图。

我已将迷宫游戏地图复制到名为“maze.dat”

的文本文件中

这是一个简单的程序,它逐行解析maze.dat文件,将每一行存储到一个名为Node的用户定义数据结构中。然后将每个节点放在另一个称为向量的数据结构中。

在程序结束时,我打印出向量中的每个节点,以便您可以看到它与原始输入文件匹配。这是我的例子:

#include <iostream>
#include <fstream>
#include <vector>

// storing each node in a data structure
class Node
{
public:
    Node(char name, char north, char east, char south, char west)
    {
        this->name = name;
        this->north = north;
        this->east = east;
        this->south = south;
        this->west = west;
    };

    char name;
    char north;
    char east;
    char south;
    char west;
};

// function to print out a node
void print_node(Node n)
{
    std::cout << n.name << " " << n.north << " " << n.east << " " << n.south << " " << n.west << " " << std::endl;
}

int main(int argc, const char * argv[])
{
    // first off let's read in our maze data file
    std::ifstream maze_file("maze.dat");

    // create somewhere to store our nodes
    std::vector<Node> nodes;

    // check that we opened the file, then parse each line
    if( maze_file.is_open() )
    {
        while( maze_file.good() )
        {
            // temporary node_data for each line in the file
            std::string node_data;

            // read the current line
            getline( maze_file, node_data );

            // parse the line into tokens (e.g. A, ,*, ,B, ,*, ,* )
            std::vector<char> tokens(node_data.begin(), node_data.end());

            // strip out the blanks ' ' (e.g. A,*,B,*,*)
            tokens.erase( std::remove(tokens.begin(), tokens.end(), ' '), tokens.end() );

            // there should be 5 tokens for a node description
            if( tokens.size() == 5 )
            {
                Node node( tokens[0], tokens[1], tokens[2], tokens[3], tokens[4] );
                nodes.push_back(node);
            }
            else
                std::cout << "There weren't 5 tokens in the node description, there were: " << tokens.size() << std::endl;
        }

        // clean-up the open file handle
        maze_file.close();
    }
    else
        std::cout << "Unable to open file maze.dat";

    // now we can prove that we've stored the nodes in the same way as they were in the file
    // let's print them out from the vector of nodes
    std::for_each(nodes.begin(), nodes.end(), print_node);

    return 0;
}

这是将文件转换为数据结构的一种非常简单的方法。这对于加载文件很有用,然后您可以创建一种保存地图的方法,从而构建地图创建程序。

当涉及到在实际游戏中实际使用迷宫地图时,这可能没什么帮助。根据您是想要向北,向东,向南,向西行驶,您更有可能想要检索相关房间。为此,您将需要使用先前由Str1101描述的std :: map构建图形数据结构