2016-07-26 6 views
4

저는 스마트 포인터를 처음 접했고 약식 연산자를 사용한 후 weak_ptr이 만료되는 이유를 제 머리로 마무리하려고합니다. 내가 테스트하는 데 사용되는 코드는 여기에 있습니다 :C++ weak_ptr가 참조 해제 된 후 만료됩니까?

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

using namespace std; 

struct node 
{ 
    weak_ptr<node> parent; 
    shared_ptr<node> child; 
    int val; 
}; 

shared_ptr<node> foo() 
{ 
    shared_ptr<node> a = make_shared<node>(); 
    shared_ptr<node> b = make_shared<node>(); 

    a->val = 30; 
    b->val = 20; 

    b->parent = a; 
    a->child = b; 

    return a; 
} 

int main() 
{ 
    shared_ptr<node> c = foo(); 
    node d = *foo(); 

    if (c->child->parent.expired()) 
    { 
     cout << "weak ptr in c has expired." << endl; 
    } 

    if (d.child->parent.expired()) 
    { 
     cout << "weak ptr in d has expired." << endl; 
    } 

    return 0; 
} 

프로그램 출력 weak ptr in d has expired.

d가 역 참조 연산자를 사용하는 경우, 만료 왜 이해가 안 돼요. 이것에 관해서는, 그것을 방지하기위한 어쨌든 (역 참조가 아닌)?


나는 shared_ptr에 노드의 weak_ptr을 변경하여 as mrtnj suggested을 시도하지만 난 메모리 누수가 생각합니다. 나는

struct node 
{ 
    shared_ptr<node> parent; 
    shared_ptr<node> child; 
    int val; 
}; 

node 클래스를 변경 한 후 tryCreate 기능을 추가 할 수있는 소스 코드를 수정했습니다.

void tryCreate() 
{ 
    node d = *foo(); 
} 

다음 내 주요 보이는

int main() 
{ 
    tryCreate(); 
    return 0; 
} 

처럼 비주얼 스튜디오 2015의 메모리 프로파일 링을 사용하는 것이 내 main 등에 전화 만 할당없이 할당 해제가 있었다 것으로 나타났습니다. parentweak_ptr으로 변경했는데 할당이 끊어졌습니다. 내가 틀린 일을하고 있습니까? 아니면 실제로 이러한 순환 조건에서 weak_ptr을 사용해야합니까?

답변

6

weak_ptr 개체를 참조하는 마지막 shared_ptr이 만료 될 때 만료됩니다. 객체 (foo에 의해 생성 된 두 가지의 부모 개체)를 참조하는 마지막 shared_ptrshared_ptr을 반환 foo()

node d = *foo(); 

여기에 문에서 일어나는 코드에서

. 그리고이 shared_ptr은 탈퇴 한 직후 임시로 파괴되었습니다. 그러면 참조 횟수가 0으로 줄어들고 weak_ptr이 만료됩니다.

shared_ptr이 마지막 이었기 때문에 개체가 파괴되어 그 개체도 파괴됩니다. 따라서 이러한 객체를 탐색하는 이후 코드는 정의되지 않은 동작을가집니다.

d은 자식 노드에 shared_ptr을 포함하고 있기 때문에이 시점에서 Miles Budnek의 주석에 명시된 바와 같이이 시점에서 자식 노드가 파괴되지 않습니다. 여기

+0

'foo '에 할당 된 부모 노드의'child' 포인터가 원래 부모가 문장의 끝에서 파괴되기 전에'd'에 복사되기 때문에 자식 노드가 파괴되지 않는다고 확신합니다 . –

+0

@MilesBudnek : 네 말이 맞아. 감사! 나는 커피를 심각하게 필요로한다. ;-) –

2

:

shared_ptr<node> a = make_shared<node>(); 

a 단지 node d = *foo(); 후 파괴됩니다

node d = *foo(); 

당신은 shared_ptr을 역 참조, 그래서 d 줄에서 foo는에서 만든 node의 복사본이 들어 있습니다. 이것은 부모가 노드 내부의 weak_ptr이기 때문입니다.

이와 관련하여 어쨌든이를 참조 해제하지 않고 있습니까?

역 참조가 적절하지 않은 것 같습니다.

weak_tr<node> parent;에서 shared_ptr<node> parent;으로 전환 할 수 있습니다. 다른 해결책은 에 대한 참조를 유지하는 글로벌 shared_ptr<node> root;을 유지하는 것입니다. 하지만 그것은 코드가 실제로 수행 할 작업에 달려 있습니다.

+0

소멸자가 호출 될 때 메모리가 할당 해제됩니다. 예를 들어,이 소멸자는 main의 끝입니다. 이것은 고전적인 RAII입니다. weak_ptr-s는 파괴를 막아서는 안되는 객체에 shared_ptr를 저장해야 할 때나 포인터의 순환 참조를 제거 할 때 유용합니다. – marcinj

+0

@mrtnj 부모를 'shared_ptr'로 설정하여 말한 것을 시도했다. 그러나 메모리 누수가있는 것으로 나타났습니다. 어쩌면 내 개념이 올바르지 않을 수도 있습니다. 내가 한 제안에 대한 편집을 참조하십시오. – silentwf

+0

@ silentwf 당신이 옳다는 것은 나쁜 제안이었습니다. 나는 대답에서 그것을 제거 할 것입니다. 루트 참조 사용을 고려 했습니까? 예 : http://coliru.stacked-crooked.com/a/24a40dcc0178d75b – marcinj

3
node d = *foo(); 

foofoo 생존에 할당 된 부모 노드를 유지하는 shared_ptr 반환합니다.

그런 다음 내용을 d으로 복사하지만 shared_ptr은 저장하지 않으므로 명령문의 끝에서 삭제됩니다. foo에 동적으로 할당 된 노드 인스턴스를 참조하는 shared_ptr 인스턴스가 없으므로 weak 포인터 참조가 만료되었습니다.

역 참조가 문제가 아닙니다. 문제는 shared_ptr을 캡처하지 못하는 것입니다.