2012-09-14 5 views
0

C++로 트리를 만드는 방법을 알아 내려고하고 있습니다. 나는 이것을 몇 시간 동안 디버깅 해보았으며, 나는 그것에 눈을 보게 될 때가되었다고 생각했다. 내 질문은 내 treeNodeClass가 올바른지 여부입니다. 지금은 노드에서 항목을 두 번 제거하기 때문에 스택 폭발이 발생합니다. 트리 자체가 간단한 xml 파일을 구문 분석합니다. 여기 내 코드가있다.C++ 트리 포인터 오류

#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; 

}

답변

3

1.A 아이들은 부모를 삭제 shouldn`t :

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. 주요 기능 tempNodenewNode 포인터를 삭제하지 마십시오.

4. 네이티브 포인터를 사용하는 경우 각 하위를 반복적으로 만들고 복사해야합니다. 다른 방법으로 메모리 누수를 잡을 수 있습니다. 방법 CopyIntoMe

5.Example :

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; 
} 
+0

좋은 지적. 나는 그것에 대해 생각하고 있지 않았다. 나는 우리가 방금 소멸자에 대한 모든 데이터를 삭제 한 OOP 교육에서 왔습니다. 이것은 나무를 가진 나의 처음이므로, 따라서 문제입니다. –

+0

Main 함수는 테스트 코드 일뿐입니다. 나는 그 부분을 정신으로 남겨 두었습니다. 여러 포인터를 저장하는 컨테이너를 삭제하는 쉬운 (또는 올바른) 방법이 있습니까? 삭제 호출을 사용하고 컨테이너의 각 n에 대해 포인터를 NULL로 설정한다고 가정합니다. –

+0

각 항목에 대한 클리어 호출 소멸자의 컨테이너입니다. 이 경우 포인터와 포인터 소멸자 유형의 항목은 메모리를 할당하지 않으며 객체의 소멸자를 호출하지 않습니다. – Lex

2

이 나쁘다 :

parent = new TreeNodeClass; 
parent = p; 

이 메모리 누출이다. 당신이 메모리를 할당하고 그것에 부모를 가리키고 있기 때문에, 부모를 다른 것에 직접 가리 키기 때문에 할당 된 메모리를 결코 삭제할 수 없습니다. AddParent()가 호출 될 때마다 메모리가 할당되고 손실됩니다.

+1

또한'CopyIntoMe' 메소드에도 있습니다. – Lex

+0

와우, 내가 그걸 놓쳤다 니 믿을 수가 없어. 웬일인지 부모님이 수업에 빠졌다는 것을 잊어 버렸습니다. 포인터를 복사 만하는 P를 부모에게 할당하면 올바른 것입니까? (포인터가있는 동안은 오래되었습니다). –

+0

예, TreeNodeClass로 전달 된 포인터를 부모 포인터로 만듭니다. – ryan0