【问题标题】:C++ Tree pointer ErrorC++ 树指针错误
【发布时间】:2012-09-14 04:02:17
【问题描述】:

我正在尝试弄清楚如何在 C++ 中创建树。我已经尝试调试了好几个小时,我认为是时候让另一双眼睛关注它了。我的问题是我的 treeNodeClass 是否正确。现在我的堆栈爆炸,因为我从我的节点中双重删除项目。树本身将解析一个简单的 xml 文件。这是我的代码。

#include "treeNodeClass.h"

TreeNodeClass::TreeNodeClass()
{
  cout << "TREENODECLASS::TREENODECLASS()" << endl;
  attributes.clear();
  children.clear();
  data = "";
  height = 0;
  parent = NULL;
  tag = "";
  cout << "EXIT TREENODECLASS::TREENODECLASS()" << endl;
}

TreeNodeClass::TreeNodeClass(const TreeNodeClass& other)
{
    cout << "TREENODECLASS::TREENODECLASS(const other)" << endl;
    parent = NULL; 
    CopyIntoMe(other); 
    cout << "EXIT TREENODECLASS::TREENODECLASS(const other)" << endl;
}

TreeNodeClass::~TreeNodeClass()
{
      cout << "TREENODECLASS::~TREENODECLASS()" << endl; 
      if(parent)
        delete parent;
      parent = NULL; 
      children.clear(); 
      attributes.clear();
      cout << "EXIT TREENODECLASS::~TREENODECLASS()" << endl;
}

void TreeNodeClass::CreateAttrib(string root, string val)
{
  string attrib = root + "=" + val;
  attributes.push_back(attrib);
}

void TreeNodeClass::CreateTag(string data, string name)
{
  tag = name;
  this->data = data;
}

list<string> TreeNodeClass::ReturnAttrib()
{
  return this->attributes; 
}

string TreeNodeClass::ReturnTag(string tag)
{
  string retTag = "";
  if(this->tag == tag)
    retTag = this->tag;
  return retTag;
}

void TreeNodeClass::AddChildren(TreeNodeClass* c)
{
if(c != NULL)
  children.push_back(c);
}

TreeNodeClass& TreeNodeClass::operator=(const TreeNodeClass& other)
{
cout << "TREENODECLASS& TREENODECLASS OPERATOR = " << endl;
if(&other != this)
{
  if(parent)
    delete parent;

  parent = NULL; 
  attributes.clear(); 
  children.clear(); 
  CopyIntoMe(other); 
}
return *this;
}

void TreeNodeClass::CopyIntoMe(const TreeNodeClass& other)
{
  cout << "Copy into me" << endl;
  tag = other.tag; 
  data = other.data; 
  attributes = other.attributes; 
  children = other.children; 
  parent = new TreeNodeClass; 
  parent = other.parent; 
  height = other.height; 
}


void TreeNodeClass::AddParent(TreeNodeClass* p)
{ 
  if(p)
  {
    parent = new TreeNodeClass;
    parent = p; 
  }
}

std::vector< TreeNodeClass* > TreeNodeClass::ReturnChildren()
{
  return children; 
}


ostream& operator<<(ostream& out, const TreeNodeClass& treeNode)
{
out << "NODE: " << treeNode.tag << " " << treeNode.data << endl;
out << "CHILDREN: " << treeNode.children.size() << endl;
out << "HEIGHT: " << treeNode.height << endl;
out << "Attributes: "; 
for(list<string>::const_iterator iter = treeNode.attributes.begin(); iter != treeNode.attributes.end(); iter++)
{
  out << *iter << " ";  
}
out << endl;
}

void TreeNodeClass::SetHeight(int h)
{
  height = h;
}

/*void function(TreeNodeClass* node)
{
  cout << node << " " << *node << endl; 

}

TreeNodeClass* function2(TreeNodeClass* node)
{

  return node; 
}

int main()
{
  cout << "STARTING PROGRAM" << endl;
  cout << "CREATING A TREE NODE CLASS " << endl;
  TreeNodeClass* newNode;
  TreeNodeClass* tempNode; 

  list<string> attribs; 

  newNode = new TreeNodeClass; 
  tempNode = new TreeNodeClass; 

  newNode->SetHeight(10); 

  cout << *tempNode << " " <<  *newNode << endl;
  tempNode->SetHeight(20); 

  cout << *tempNode << "\n " <<  *newNode << endl;

  cout << "Setting equal " << endl;
  *tempNode = *newNode; 
  cout << *tempNode << " " <<  *newNode << endl;

  tempNode->SetHeight(40); 
  cout << *tempNode << " " <<  *newNode << endl;

  tempNode->AddChildren(newNode); 
  newNode->AddParent(tempNode); 
  cout << *tempNode << "\n " <<  *newNode << endl;

  return 0;
}
*/

我正在尝试在一个简单的状态机上使用此代码。我基本上设置了一个列表向量来返回状态。这就是我认为给了我大部分错误的原因。我也盯着这个看了一段时间,但我有点迷路了。机器将创建树(据说)。当状态机完成(状态 10)时,它返回并且调用函数将再次调用 yylex()。感谢您迄今为止的帮助!

TreeNodeClass* ProcessTree(TokenT token, vector <list <stateAssoc> >& vecTree, int depth)
   {
int state = 1; //Assume this is a new record.
bool noState = false;
bool update = true;
int dex = 0;
string root, value, data, tag;
TreeNodeClass* treeNode;

treeNode = new TreeNodeClass; //Assume a new node per state machine visit.


while(state != 10)
{
  switch(state)
  {
case 1: dex = 1;
    break;

case 2: state = 3;
    noState = true;
    root = yylval;
    break;

case 3: state = 4;
    noState = true;
    break;

case 4: dex = 3;
    value = yylval;
    //cout << value << endl;
    treeNode->CreateAttrib(root, value);
    break;

case 5: dex = 2;
    data = yylval;
    //cout << 5 << " " << yylval  << " " << token << endl;

    //If its data store as data; if tag its a start tag.
    break;

case 6: dex = 4;
    //cout << token << " " << yylval << endl;
    break;

case 7: state = 9;
    noState = true;
    tag = yylval;
    //cout << tag << token << endl;
    if(data != "" and data != "authors")
      treeNode->CreateTag(data, tag);
    break;

case 8: {
      TreeNodeClass* childNode = new TreeNodeClass;
      childNode = ProcessTree(token, vecTree, depth+1);

      cout << "BEFORE PARENT" << endl;
      childNode->AddParent(treeNode);
      childNode->SetHeight(depth); 
      treeNode->AddChildren(childNode);
      delete childNode;
      childNode = NULL;
    }
    token = TokenT(yylex()); //Get a new token to process.
    dex = 5;
    break;

case 9: state = 10;
    noState = true;
    update = false;
    break;

case 10: noState = true;
    update = false;
    break;

default: cout << "Error " << endl;
    cout << state << endl;
    cin.get();
    break;

  }

  if(!noState)
state = FindMatch(vecTree[dex], token);
  else
noState = false;

  if(update)
token = TokenT(yylex());
  else
update = true;
}
return treeNode;

}

【问题讨论】:

    标签: c++ pointers tree runtime-error


    【解决方案1】:

    1.孩子不应该删除父母:

    TreeNodeClass::~TreeNodeClass()
    {
          cout << "TREENODECLASS::~TREENODECLASS()" << endl; 
          /* Delete next 2 lines
          if(parent)
            delete parent;
          */
          parent = NULL; 
          children.clear(); 
          attributes.clear();
          cout << "EXIT TREENODECLASS::~TREENODECLASS()" << endl;
    }
    

    2.Containers 不会删除指针——你应该时刻牢记这一点。简单的删除方式,例如:

     for (vector<TreeNodeClass*>::iterator child = children.begin(); child != children.end(); ++child)
         delete *child;
    

    但最好的方法——不要使用本机指针,而是使用一些智能指针或共享指针。

    3.Main函数不删除指针tempNodenewNode

    4.如果您将使用本机指针,您应该递归地创建和复制每个孩子。否则你会发现内存泄漏。

    5.CopyIntoMe方法示例:

    void TreeNodeClass::CopyIntoMe(const TreeNodeClass& other)
    {
      cout << "Copy into me" << endl;
      tag = other.tag; 
      data = other.data; 
      attributes = other.attributes; 
    
      // delete each pointer to Nodes
      foreach (vector<TreeNodeClass*>::iterator child = children.begin(); child != children.end(); ++child)
        delete *child;
      children.clear();
    
      // Copy recursively each child
      foreach (vector<TreeNodeClass*>::iterator child = other.children.begin(); child != other.children.end(); ++child) {
        TreeNodeClass* new_child = new TreeNodeClass;
        new_child->CopyIntoMe(*child);
        children.push_back(new_child);
      }
    
      parent = other.parent; 
      height = other.height; 
    }
    

    【讨论】:

    • 好点。我没有这么想。我来自我的 OOP 培训,我们刚刚在析构函数时删除了所有数据。这是我第一次接触树木,因此出现了问题。
    • Main 函数只是测试代码。我只是把那部分留给了精神。是否有任何简单(或正确)的方法来删除存储多个指针的容器?我假设我会使用删除调用,然后将容器中每个 n 的指针设置为 NULL。
    • 容器对每个项目的明确调用析构函数。在这种情况下,类型指针和指针析构函数中的项不会释放分配的内存,也不会调用对象的析构函数。
    • 如果state == 8您的代码:创建新的TreeNodeClass-object,记住指向它的指针并删除这个对象,但指向它的指针保存在容器children中。你应该看到我回答的第 2 点和第 4 点。
    • 在您的情况下,最好的方法是创建节点对象,在不删除的情况下推入children。在 children.clear() 附近的两个地方调用 delete:析构函数和方法 CopyIntoMe。你也应该修复方法CopyIntoMe(我会在答案中写示例)
    【解决方案2】:

    这很糟糕:

    parent = new TreeNodeClass;
    parent = p; 
    

    这是内存泄漏。由于您正在分配内存并将父级指向它,然后立即将父级指向其他东西,因此您永远无法删除分配的内存。每次调用 AddParent() 时都会分配和丢失内存。

    【讨论】:

    • 还有CopyIntoMe方法。
    • 哇,我不敢相信我错过了。由于某种原因,我忘记了父母在课堂上。如果我将 parent 分配给 p 那将只复制指针正确吗? (指针已经有一段时间了)。
    • 是的,它会使 parent 成为指向传入的 TreeNodeClass 的指针。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-15
    • 1970-01-01
    • 1970-01-01
    • 2014-04-12
    • 2015-06-24
    相关资源
    最近更新 更多