2016-07-21 8 views
1

저는 C++을 처음 접했고 LinkedList를 구현하여 언어에 익숙해졌습니다. 및 생성자 (에는 다음 요소가 존재하지 않는 경우 nullptr)getter를 통해 액세스되는 C++ 클래스 멤버가 쓰레기를 생성하지만 직접 액세스는 좋지만 std :: cout은 간섭합니까?

class ListElement { 
public: 
    int val; 
    ListElement *next; 

    ListElement(int v, ListElement *n) {val = v; next = n;}; 
}; 

ListElement는 int 값 val 및 다음 목록 요소에 대한 포인터를 포함한다.

class MyLinkedList { 
public: 
    ListElement *head; 

    MyLinkedList() {head = nullptr;}; 

    ListElement* getHead(void){ 
     return head; 
    }; 

    void append(int i) { 
     head = &ListElement(i, head); 
    }; 
}; 

MyLinkedList는 몇 가지 방법이 목록에 작업뿐만 아니라 head 이름 목록의 첫 번째 요소에 대한 포인터를 포함합니다. 그 방법들에서 몇 가지 버그들을 만났을 때 그 원인을 추적하려고했습니다. (I 공개 클래스 멤버에 대한 게터 전혀 원래 head 개인이었다 아무 의미가 없다는 것을 알고 있어요.) 나는 다음과 같은 행동을 관찰 그렇게 내가 설명 할 수 없다 :

int main() { 
    MyLinkedList l; 
    l.append(1); 

    int x = l.head->val; 
    cout << "head: " << x << "\n"; 
    int y = l.getHead()->val; 
    cout << "getHead(): " << y << "\n"; 
    int z = l.head->val; 
    cout << "head: " << z << "\n"; 

    cin.get(); 
    return 0; 
} 

이 코드를 실행 (추가 작업 예제)에 대한 #include <iostream>using namespace std;은 첫 번째 목록 요소의 값으로 1를 산출, 그래서 head 작품의 첫 번째 직접 액세스가 예상대로

head: 1 
getHead(): 18085840 
head: -858993460 

를 인쇄하지만, 게터를 사용하여 쓰레기를 반환합니다. head는 본 뒤 다시 액세스하는 경우, 그것은 또한, "흠을 어떻게 든 ListMember 객체를 깨짐) getHead를 (사용하는 것 같다"나를 생각하게 쓰레기를 산출 단지 발견, 그

int x = l.head->val; 
cout << "head: " << x << "\n"; 
int z = l.head->val; 
cout << "head: " << z << "\n"; 

인쇄

head: 1 
head: -858993460 

게터를 건드리지 않고도. 그렇다면 단순히 l.head에 액세스하면됩니다. (의도 한대로)

없음

,

int x = l.head->val; 
int z = l.head->val; 
cout << "head: " << x << "\n"; 
cout << "head: " << z << "\n"; 

로 돌아 head: 1 두 번. 그 사이에 cout을 사용하면 내 객체 또는 포인터가 변경됩니까? 그리고 getHead()의 모든 점이 단지 return head; 일 뿐이므로 잘못되었습니다.


저는 여기에서 꽤 분실되어 직접적인 관련 질문을 찾을 수 없습니다. (This Question에는 유망한 제목이 있지만 포인터는 사용하지 않습니다.) 올바른 방법으로 포인터를 사용하지 못하고 있습니까? 또는 자동 개체 삭제가 뒤에서 진행되고 있습니까? 아니면 magiC++가 어떻게 작동합니까? 이 경우, 컴파일

+2

'head = & ListElement (i, head);'- 새 ListElement를 할당하는 방법이 아닙니다. 'new'에 대해 읽으십시오 (그리고 대부분의 경우, 필요하지 않도록 노력하십시오). – user2357112

+1

좋은 측정을 위해, 당신은 또한 [스마트 포인터] (http://stackoverflow.com/questions/106508/what-is-a-smart-pointer-and-when-should-i-use-one)에 대해 알고 싶을 수도 있습니다.) – jaggedSpire

+0

@ jaggedSpire 맞아, 내 잘못 - 똑바로 생각하기에는 너무 늦었다. – jpw

답변

1

C++에서 사용자는 자신의 메모리를 관리해야합니다.문

ListElement(i, head); 

MyLinkedList :: append()를 로컬 범위로 ListElement의 인스턴스를 만듭니다. 따라서 일단 함수가 종료되면 변수는 더 이상 존재하지 않으며 포인터는 현재 유효하지 않은 메모리를 가리 킵니다.

처음 인쇄본이 옳은 답을주는 이유는 빨간 청어입니다. 모든 경우에 정의되지 않은 동작이있는 자유로운 메모리에 액세스하고 있습니다. 첫 번째 경우에는 메모리가 이전에 설정 한 값을 갖게됩니다.

추가 할 때 자신의 메모리를 할당하고 작업을 마친 후에는이를 정리해야합니다. "새"를 마스터하고 나면 데이터 구조를 반복하고 각 요소를 삭제하는 방법을 찾아야합니다. 링크 된 구현 목록을 사용하면이 작업이 매우 간단합니다.

void append(int i) { 
    head = &ListElement(i, head); 
}; 

ListElement(i, head)에서

1

변화

void append(int i) { 
    head = &ListElement(i, head); 
}; 

void append(int i) { 
    head = new ListElement(i, head); 
}; 

처음에, 할당 된 임시 스택 객체의 주소를 가지고있다. 따라서 head은 파괴 된 후 "쓰레기를 가리 킵니다".

2

임시, 이름없는 ListElement를 만들고 head에 대한 포인터를 지정합니다. 그런 다음 ListElement 자체에 할당되지 않았기 때문에 ListElementgoes out of scope이 파괴됩니다. 이로 인해 잘못된 메모리를 가리키고 있습니다.

헤드 정도에 ListElement

void append(int i) { 
    head = new ListElement(i, head); 
}; 

의 수명을 동적 메모리를 사용하여 작성할 수 있지만, 지금은 사람이 더 이상 필요하지 않을 때 ListElement가 삭제되는 것을 보장하는 책임을 데리러있다. 예를 들어

: std::unique_ptrstd::move

void remove(int i) { 
    // find list element i and previous element. Special handling required for first element 
    prev.next = element.next; 
    delete element; 
}; 

주의 깊은 사용은 메모리 관리를 자동화 얻을 delete에 대한 필요성을 제거 할 수 있습니다.