2011-09-27 10 views
8

이것은 불가능할 수도 있지만 일시적으로 원래의 표현보다 오래 지속될 수 있는지 궁금합니다. 나는 부모 객체를 가리키는 객체의 체인을 가지고 있고, 자식 객체를 생성하는 멤버 함수는, 단순화 된 예는 여기일시적으로 수명이 연장되지 않도록 하시겠습니까?

class person{ 
    string name; 
    person * mommy; 
public: 
    person(const string & nam, person * m = 0) : name(nam), mommy(m) {} 
    person baby(const string & nam){ 
     return person(nam, this); 
    } 
    void talk() const{ 
     if (mommy) mommy->talk(); 
     cout << name << endl; 
    } 
}; 

int main(){ 
    person("Ann").baby("Susan").baby("Wendy").talk();  // fine 

    const person & babygirl = person("Julie").baby("Laura"); // not fine 

    babygirl.talk(); // segfault 
    return 0; 
} 

내가 person 함수에 전달하는 데 사용하려는 방법입니다 다음과 같은 내용이 있습니다.

void use(const person & p) { 
    p.talk(); 
} 
use(person("Anna").baby("Lisa")); 

괜찮습니까?

임시 테이블이 원래 표현식보다 오래 살아남지 않는 한 정상적으로 작동합니다.하지만 마지막 임시 테이블 중 하나를 const 참조에 바인딩하면 해당 부모가 살아남지 않고 segfault가 생깁니다. person의 복사본 생성자와 할당 연산자를 숨길 수는 있지만 이런 종류의 오류가 발생하는 것을 막을 수있는 방법이 있습니까? 가능한 경우 동적 할당을 피하고 싶습니다.

+0

@Konrad : Ironic; -] – ildjarn

+1

이 코드는 같은 방식으로 "좋지 않습니다" 'const int & i = std :: vector (1) [0]; '을 쓰는 것은 "좋지 않습니다"라고 말합니다. 'vector'는 당신이 글을 쓰는 것을 멈추지 않으며, 그렇게 할 필요가 없습니다. 여기서 핵심은 엄마를 파괴하면 아기를 사용할 수 없게되므로 아기는 엄마의 일부입니다. 그것이 디자인에있어 잘못된 것입니다. 반 직관적입니다. 고아와 같은 것이 존재하지 않도록 방지함으로써 패치를 적용하려고합니다. 그러나 고아가 더 잘 정의 된 행동을 가져야하는지 또는 더 분명히 나쁜 것으로 만들어야하는지 고려해야합니다. –

+0

Steve와의 합의 : 이것은 잘못된 디자인을 보여주는 것입니다.벌거 벗은 포인터의 모양은 뭔가 떨어져 있다는 것을 알려 줘야합니다. –

답변

3

여기에 자녀가 부모를 가리키는 포인터가있는 데이터 구조를 만드는 것처럼 보입니다. 임시 직원을 사용하면이 경우 슬픔을 느끼게됩니다. 이것을 안전하게하려면 동적으로 할당해야하며 일종의 참조 계산을 사용해야합니다.

boost::shared_ptr을 사용 해본 적이 있습니까? 레퍼런스 카운트 된 스마트 포인터 클래스의 구현입니다. shared_ptr과 일부 팩토리 메소드를 사용하면 원하는 효과를 얻고 동적 메모리 할당의 고통을 줄일 수 있습니다. 나는 그것을 시도하고 그것이 작동하는 것 같습니다. 코드가 스코프를 벗어나면 shared_ptrs에 남아있는 참조가 없으므로 오브젝트가 모두 삭제됩니다.

편집 : 루트 개체는 데이터 구조의 수명을 제어하도록 응답에서 제기랄 ' 코멘트 에, 나는 예를 수정했습니다.

#include <iostream> 
#include <string> 
#include <vector> 
#include <boost\shared_ptr.hpp> 
#include <boost\weak_ptr.hpp> 

using boost::shared_ptr; 
using boost::weak_ptr; 

using std::string; 
using std::cout; 
using std::endl; 
using std::vector; 

class person; 
typedef shared_ptr<person> Person; 
typedef weak_ptr<person> PersonWk; 

class person {  
    PersonWk pThis; 
    friend Person makePerson(const string & nam, Person m = Person()); 

    string name; 
    PersonWk mommy; // children should not affect parent lifetime, so store weak ptr 
    vector<Person> children; // parents affect children lifetime so store child shared ptrs 

    // make constructor private so that you can only make a person using factory method 
    person(const string & nam, Person m) : name(nam), mommy(m) 
    { 
     // for demo purposes 
     printf("creating %s\n", nam.c_str()); 
     ++personCount; 
    } 

    // undefined copy constructor and assignment operators 
    person(const person&); 
    person& operator=(const person&); 

public: 
    // for demo purposes 
    static int personCount; 

    ~person() 
    { 
     // for demo purposes 
     printf("destroying %s\n", name.c_str()); 
     --personCount; 
    } 

    Person baby(const string & nam){   
     Person child = makePerson(nam, Person(pThis)); 
     children.push_back(child); 
     return child; 
    } 

    void talk() const{ 
     if (Person mom = mommy.lock()) 
      mom->talk(); 
     cout << name << endl; 
    } 
}; 

int person::personCount = 0; 

// factory method to make a person 
Person makePerson(const string & name, Person m) { 
    Person p = Person(new person(name, m)); 
    p->pThis = p; // stash weak_ptr so I can use it to make a shared_ptr from "this" in the baby method 
    return p; 
} 

void use(const Person p) { 
    printf("In method use...\n"); 
    p->talk(); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    printf("personCount=%d\n", person::personCount); 
    { 
     Person ann = makePerson("Ann"); 

     // ann has baby and grandbaby, pass grandbaby to use method 
     use(ann->baby("Susan")->baby("Wendy")); 

     ann.reset(); // remove reference from root object. Destruction ensues... 
    } 
    printf("personCount=%d\n", person::personCount); 
    return 0; 
} 
+0

답변 해 주셔서 감사합니다. 공유 된 ptr과 함께 나를위한 문제는 루트 객체가 특정 파괴 점을 가져야 할 수도 있고, 어딘가에 저장된 포인터가 있을지 걱정하지 않으려한다는 것입니다. 나는 임시 함수가 호출 될 때만 존재할 필요가 있습니다. 그리고 나서 그들은 일시적으로 죽을 수 있습니다. 아이들이 부모에게 영향을 미치지 못하도록하는 child-> parent 구조체를 사용하고 있습니다 (모든 사람들이 사용할 수있는 일반 공용 인터페이스 제외). – zounds

+0

하나의 접근법은 알려진 위치에 루트 객체를 저장할 수 있습니다. 그런 다음 각 부모가 shared_ptrs를 자식에게 보관하게하고 자식이 weak_ptrs를 부모에게 보관할 수있게합니다. 그런 다음 루트 객체를 NULL로 설정하면 모든 참조가 제거되므로 트리가 위에서 아래로 파괴됩니다. –

+0

이전 주석에서 접근법을 사용하기위한 수정 된 코드 샘플. –

0

당신은 이런 식으로 뭔가를해야 할 것이다 :

void use(const person & p) { 
    p.talk(); 
} 
person a("Anna"); 
use(a.baby("Lisa")); 

당신이 진정 후 (그것으로 완료 될 때까지, 부모가 "A"범위를 벗어난하지 않는 방법 "사용"이라고 부름).

원래 코드의 문제는 "Anna"라는 부모가 "baby"를 호출하기에 충분히 오래 있어야하고 부모는 함수 호출을하기 전에 삭제 될 수 있다는 것입니다. 상위 변수를 범위로 변경하면 언제 소멸되는지 제어 할 수 있습니다.

나에게 위험한 것으로 보입니까? 예. 그러므로 동적 할당에 대한 m-sharp의 대답을 살펴볼 것을 제안합니다. 하지만 참조 계산이 필요하지 않은 메서드를 원한다면 다음과 같이하면됩니다.

관련 문제