【问题标题】:How to get a child in an n-way tree?如何让一个孩子在一棵n路树上?
【发布时间】:2015-01-22 06:02:47
【问题描述】:

我有一个使用 std::vector 实现的 n 路树。 Node 类只保存指向根元素的指针,而 Link 类有一个 std::vector 指向自身的指针。我在创建每个链接时命名了它们,但是当我尝试使用链接名称在此树中定位节点时却出现分段错误。我也意识到我的函数 GetLink() 没有做任何错误检查,我尝试了几件事,但它们没有工作,所以如果可能的话,如果有任何关于如何在这种情况下实现它的建议,将不胜感激。这是我的代码:

// in node.h
class Node {
public:
 // constructors
 // fuctions
private:
 Link *root;
};

// in link.h
class Link {
public:
//EDIT: added vector initialization in the constructor
 Link() : my_links(0) { }
 // some functions
 // EDIT: example of making the tree
 bool Load(token) {
 // parsing code based on token
   else if (strcmp (temp, "link") == 0)
   {
        Link* lnk = new Link ();
        lnk->Load (token);
        lnk->Init ();
        AddChild (lnk);
        lnk->m_parent = this;
   }
    // some more parsing code
 }
 void Link::AddChild (Link* pChild)
 {
   my_links.push_back(pChild);
 }

 Link* Link::GetLink(char* str)  // this is the function that is the problem.
 {
   if (strcmp(name, str) == 0)
   {
     return this;
   }

   for (int i=0; i < (int) my_links.size(); i++)
   {
    //Edit: added check for NULL ptr
     if (my_links[i] == NULL)
     {
        fprintf (stderr, "\n\t Couldn't find link\n\n");
        break;
     } 
     //Edit: typo corrected
     return my_links[i]->GetLink(str);
   }
 }

private:
 char name[256];
 Link* m_parent;
 std::vector<Link*> my_links;
};

// in main.cpp
static Node* node;
static Link* link;
main()
{
  char *str = "link_3";
  link = node->GetLink(str);
  printf("\n found link: %s", link->GetName());
  retrun 0;
}

编辑:将早期代码重写为 MCVE

#include <cstdio>
#include <vector>
#include <cstring>

class Link {
public:
//EDIT: added vector initialization in the constructor
 Link() : my_links(0)
 {
   m_parent = NULL;
 }

 void SetParent(Link* pParent)
 {
   m_parent = pParent;
 }

 // EDIT: example of making the tree
 bool Load(char *str)
 {
        unsigned int len;
        Link* lnk = new Link ();
        len = strlen(str);
        strcpy(name, str);
        lnk->SetParent(this);
        AddChild (lnk);
        return true;
 }

 void AddChild (Link* pChild)
 {
   my_links.push_back(pChild);
 }

 Link* GetLink(char* str)  // this is the function that is the problem.
 {
   if (strcmp(name, str) == 0)
   {
     return this;
   }

   for (int i=0; i < (int) my_links.size(); i++)
   {
    //Edit: added check for NULL ptr
     if (my_links[i] == NULL)
     {
        fprintf (stderr, "\n\t Couldn't find link\n\n");
        break;
     }
     //Edit: typo corrected
     return my_links[i]->GetLink(str);
   }
   fprintf(stderr, "\n\t Cannot get link\n\n");
   return 0;
 }

 char* GetName()
 {
   return name;
 }

private:
 char name[256];
 Link* m_parent;
 std::vector<Link*> my_links;
};


class Node {
public:
 Node()
 {
    root = NULL;
 }

 bool Load (char *str)
 {
    unsigned int len;
    root = new Link();  // here is where the error occurs
    len = strlen(str);
    strcpy(name, str);
    return true;
 }

 void AddChild (char *str)
 {
   root->Load(str);
 }

 Link* GetRoot()
 {
   return root;
 }

private:
 char name[256];
 Link *root;
};

static Node* node;
static Link* lnk;
int main()
{
  node->Load((char*)"I am root");
  node->AddChild((char*)"I am child 1");
  node->AddChild((char*)"I am child 2");
  node->AddChild((char*)"I am child 3");
  char *str = (char*)"I am child 2";
  lnk = node->GetRoot()->GetLink(str);
  printf("\n found link: %s", lnk->GetName());
  return 0;
}

我现在在 VS2010 的第 77 行遇到错误,即 Node 类中的“root = new Link()”,Load() 函数是:

Unhandled exception at 0x012e1bbe in nWayTree.exe: 0xC0000005: Access violation writing location 0x00000100.

【问题讨论】:

  • 如果你使用std::vector,你写的不是C,所以不要用C标记你的问题。
  • 你还没有初始化vector的大小。要么push_back所有Link指针,要么在构造函数中初始化vector的大小。因此,my_links.size() 给出了分段错误。
  • @ShauryaChats 我在构造函数中初始化向量,并有一个通过 push_back 添加子项的函数,为了简洁起见,我没有在此处包含它。
  • 似乎内存管理会很痛苦。在你的向量中使用 boost::optional 或 boost::any。或智能指针,如unique_ptr。这将使您不必手动关心 RAII。
  • 好的。另外,我认为您应该尝试在GetLink() 中添加终止条件,以使Link* 等于NULL

标签: c++ tree


【解决方案1】:

您在 MCVE 中收到错误的原因是您从未实例化 node,因此,node-&gt;Load((char*)"I am root"); 取消引用未初始化的指针,从而导致未定义的行为。实际上,当您的程序尝试写入它期望“根”成员变量存在的内存位置时,您会遇到访问冲突。您可以通过编写

来解决此问题
static Node* node = new Node();

但是,即使修复了此错误,您的 MCVE 仍然存在语义错误。每次调用 node-&gt;AddChild 时,您都不会添加另一个子节点,而是重命名节点并将新的默认构造链接(没有名称)分配给节点的 root 成员变量。此外,您的 GetLink() 函数不会对所有子级(如果有的话)递归调用 GetLink(),而只会在第一个子级上调用 GetLink(),然后无条件返回结果(循环最多执行一次)。

Node、Link 和 root 之间的区别我也不是很清楚。这就是我假设您想要实现的目标,尽可能少地使用额外的 c++(与您的示例相比):

#include <cstdio>
#include <vector>
#include <cstring>

class Node {
private:
    char m_name[256];
    Node* m_parent;
    std::vector<Node*> my_links;
public:
    //EDIT: added vector initialization in the constructor
    Node(const char* name, Node* parent=nullptr) : m_parent(parent), my_links() {
        strcpy(m_name, name);
    }

    void AddChild(const char* childname) {
        my_links.push_back(new Node(childname,this));
    }

    Node* GetNode(const char* str)  {
        if (strcmp(m_name, str) == 0)   {
            return this;
        }
        for (size_t i = 0; i < my_links.size(); i++) 
        { 
            Node* t = my_links[i]->GetNode(str); 
            if (t != NULL) return t; 
        } 
        return NULL; 

    }

    char* GetName() {
        return m_name;
    }
};

Node root((const char*)"I am root");

int main()
{
    root.AddChild("I am child 1");
    root.AddChild("I am child 2");
    root.AddChild("I am child 3");  
    const char *str1 = "I am child 2";
    const char *str2 = "I am child 1 of child 2 of root";
    root.GetNode(str1)->AddChild(str2);
    Node* node = root.GetNode(str2);
    if (node == NULL) {
        printf("Link not found");
    } else
    {
        printf("\n found link: %s", node->GetName());
    }
}

这是一个“现代 c++ 风格”的版本(并不是说它特别好):

编辑:我刚刚意识到您正在使用 VS2010,它将无法编译以下示例,因为它使用了一些 c++11 和 c++14(当前 c++ 标准)功能。但是,您可以使用免费版本的 VS2013 构建它,并且应该能够使用任何最新版本的 g++ 和 clang++ 构建它(尽管您必须添加标志 '-std=c++14' 或类似的)。

#include <iostream>
#include <vector>
#include <string>
#include <memory>

using namespace std;

class Node {
private:
    string m_name;
    Node* m_parent;
    vector<unique_ptr<Node>> my_links;

public: 
    Node(const string& name, Node* parent = nullptr) : 
        m_name(name),
        m_parent(parent), 
        my_links()
    {}

    void AddChild(const string& childname) {
        my_links.push_back(make_unique<Node>(childname, this));
    }

    Node* GetNode(const string& str)  {
        if (m_name == str)  {
            return this;
        } 
        for (auto& e:my_links) 
        { 
            Node* t = e->GetNode(str);
            if (t != nullptr) return t; 
        } 
        return nullptr;         
    }

    string GetName()    {
        return m_name;
    }
};

Node root("I am root");

int main()
{
    root.AddChild("I am child 1");
    root.AddChild("I am child 2");
    root.AddChild("I am child 3");
    string str1("I am child 2");
    string str2("I am child 1 of child 2 of root");
    root.GetNode(str1)->AddChild(str2);
    Node* node = root.GetNode(str2);
    if (node == nullptr) {
        cout <<"Link not found";
    } else  {
        cout << "found link: "<< node->GetName();
    }
}

【讨论】:

  • 谢谢,我也有VS2013所以没问题,感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-17
  • 1970-01-01
  • 2016-05-19
  • 1970-01-01
相关资源
最近更新 更多