【问题标题】:C++: How do I "store" nodesC++:我如何“存储”节点
【发布时间】:2013-07-25 00:34:24
【问题描述】:

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

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

第一列是当前节点。第二列,其正北的节点。第三,紧邻东部,第四,紧邻南部,第五,紧邻西部。任何 * 表示没有节点,指针应设置为 null。

请记住,我是 OOP 的新手,我很好奇如何将这些信息“保存”到名为 Node 的类中。

我有以下类定义:

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

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

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

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

【问题讨论】:

  • 您是在问如何将节点链接在一起?
  • 我不太确定我在问什么。我想是这样。我正在从文本文件中读取所有这些信息,但是一旦到达行尾,我希望保留当前保存的信息,然后为下一行创建一个 Node 类型的新对象。
  • 也许你会发现下一个问题的答案很有用:stackoverflow.com/questions/2323929/…

标签: c++ pointers linked-list nodes


【解决方案1】:

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

pair<char, Node> node_with_name;

然后您可以将名称分配给执行build &gt;&gt; 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/

【讨论】:

  • 我认为这很接近。但是,我仍然不确定到底发生了什么。我用你给我看的代码的 sn-ps 编译了程序,但是 node_with_name 是什么?当我尝试计算 A.name 或 A.east 时,编译器告诉我没有声明。我在这里想念什么?
     node_with_name;做{构建>> node_with_name.first;构建 >> node.name >> node.north >> node.east >> node.south >> node.west;节点.push_back(node_with_name); } while(!build.eof()); 
  • 我之前编辑过答案,告诉我它是否适合您的需要。
【解决方案2】:

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

【讨论】:

  • 但是我不应该有 12 个不同的节点吗?就目前而言,我的程序只有一个。除非我每次在循环中创建一个新的。如果可能的话,我想给节点命名,不管行中的第一个字符是什么。
【解决方案3】:

对您来说很困难的一件事是您不能直接创建节点,因为在早期您需要获取指向尚未创建的节点的指针。使这个问题没有实际意义的一种方法是您似乎正在做的事情:存储节点字母而不是指针:

#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 更强大的解析选项。如果您认为编写 Backus-Naur 形式 (BNF) 没什么大不了的,并且想看看在 C++ 中使用运算符重载可以做哪些疯狂的事情,请查看 Boost::Spirit。

【讨论】:

    【解决方案4】:
    #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
    

    【讨论】:

      【解决方案5】:

      据我了解,您希望能够从文件中读取节点的描述并将它们存储在内存中的数据结构中。貌似是文字冒险地图。

      我已将您的迷宫游戏地图复制到名为“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 构建图形数据结构

      【讨论】:

        猜你喜欢
        • 2020-07-16
        • 1970-01-01
        • 2014-01-16
        • 1970-01-01
        • 2021-07-13
        • 2015-08-18
        • 1970-01-01
        • 2012-03-17
        • 1970-01-01
        相关资源
        最近更新 更多