2010-03-24 6 views
18

나는 생성자 내에 다른 자식 객체를 생성하는 객체를 'this'를 전달하여 자식이 포인터를 부모 객체에 다시 저장할 수 있도록합니다. boost :: shared_ptr을 std :: auto_ptr 또는 원시 포인터에 대한 안전한 대안으로 광범위하게 사용합니다. 따라서 자식은 shared_ptr<Parent>과 같은 코드를 가지며 boost는 부모가 자식에게 줄 수있는 메서드 shared_from_this()을 제공합니다.생성자에서 'this'포인터를 처리하는 방법은 무엇입니까?

제 생각에는 shared_from_this()은 생성자에서 사용할 수 없습니다. 왜냐하면 실제로 '범죄'가 아니라 생성자에서 사용할 수 없기 때문에 어쨌든 생성자에서 사용하지 않아야하며 제한을 신경 쓰지 않는 한 .

Google의 C++ 스타일 가이드 states 생성자는 멤버 변수를 초기 값으로 설정하면됩니다. 복잡한 초기화는 명시적인 Init() 메소드에 있어야합니다. 이것은 '생성자에서 this - in - constructor'문제뿐만 아니라 몇 가지 다른 문제를 해결합니다.

귀찮은 것은 여러분의 코드를 사용하는 사람들이 여러분의 객체 중 하나를 만들 때마다 Init()를 호출해야한다는 것입니다. 내가 이것을 시행 할 수있는 유일한 방법은 Init()가 이미 모든 멤버 함수의 최상단에서 호출되었다는 주장을하는 것이지만, 이것은 작성하기가 번거롭고 실행이 번거롭다.

도중에 어떤 단계에서도이 문제를 해결할 수있는 관용구가 있습니까?

+17

Google이 잘못되었습니다. comp.lang.C++에 대한 자신들의 스타일 가이드의이 짧은 설명에 거대한 글이 있습니다. 그들이 (예외적으로) 예외를 금지하기 때문에 이것에 대한 기본적인 이유가 있습니다. –

+3

@Neil : 내가 얻은 인상은 C++을 잘 모르는 사람들, 그들이 할 수있는 피해를 줄이려고 시도하는 것, 레거시 코드로 일관성을 유지하는 사람들을 수용하기 위해 스타일 가이드를 다루는 것이 었습니다. Google은 스타일 가이드에 대한 충분한 이유가있을 수 있지만 다른 사람이 복사 할 수있는 좋은 모델은 아닙니다. –

+9

@David 맞습니다. 그러나 사람들은 부동 소수점 숫자 (주제에 대한 모든 질문을 살펴보십시오) 또는 정수를 사용하여 혼란 스러울 수 있지만 사용을 금지하지는 않습니다. 이것이 스타일 가이드의 팬이 아닌 이유 중 하나입니다. –

답변

16

2 단계 구조로 & 클래스를 초기화 한 다음 & Init() 함수를 private으로 만드는 팩토리 메서드를 사용합니다. 그렇다면 객체를 잘못 작성하는 방법은 없습니다. 그냥 소멸자 대중을 유지하기 위해 기억하고 스마트 포인터를 사용 :

#include <memory> 

class BigObject 
{ 
public: 
    static std::tr1::shared_ptr<BigObject> Create(int someParam) 
    { 
     std::tr1::shared_ptr<BigObject> ret(new BigObject(someParam)); 
     ret->Init(); 
     return ret; 
    } 

private: 
    bool Init() 
    { 
     // do something to init 
     return true; 
    } 

    BigObject(int para) 
    { 
    } 

    BigObject() {} 

}; 


int main() 
{ 
    std::tr1::shared_ptr<BigObject> obj = BigObject::Create(42); 
    return 0; 
} 

편집 :

당신은 스택에 살고 반대하려면, 당신은 위의 패턴의 변형을 사용할 수 있습니다. 이렇게 작성하면 임시로 복사본을 만들 수 있습니다.

#include <memory> 

class StackObject 
{ 
public: 
    StackObject(const StackObject& rhs) 
     : n_(rhs.n_) 
    { 
    } 

    static StackObject Create(int val) 
    { 
     StackObject ret(val); 
     ret.Init(); 
     return ret; 
    } 
private: 
    int n_; 
    StackObject(int n = 0) : n_(n) {}; 
    bool Init() { return true; } 
}; 

int main() 
{ 
    StackObject sObj = StackObject::Create(42); 
    return 0; 
} 
+0

그건 그렇고, 나는이 장치에 대한 정확한 용어가 "공장 방법"이라는 것을 확신하지 못한다. 그러나 나는 항상 그것을 부르고있다. –

+2

이 용어는 정확하고 더 잘 알고 있지만 Create에서 스마트 포인터를 반환합니다. –

+1

물론 단점은 스택에 개체를 놓을 수 없다는 것입니다. –

8

Google의 C++ 프로그래밍 가이드 라인은 여기 및 다른 곳에서 반복적으로 비판되었습니다. 그리고 맞습니다.

래핑 클래스 뒤에 숨겨진 경우에만 2 단계 초기화를 사용합니다. 수동으로 초기화 함수를 호출하면 작동 할 것이고, 우리는 여전히 C 및 C++로 프로그래밍 할 것입니다. 생성자는 결코 발명되지 않았을 것입니다.

+3

+1. Google 가이드 라인은 주로 C + + 커뮤니티 전체가 아닌 해당 사용자의 용도로 작성되었으며 새로운 일반 프로젝트를 작성하는 방법을 안내하기보다는 기존 코드 및 사례를 모두 고려해야한다는 것을 기억해야합니다. –

0

공장 출근과 같이 복잡한 구성 사운드가 필요한 객체입니다.

인터페이스 또는 추상 클래스, 즉 구성 할 수없는 추상 클래스와 매개 변수가 포함 된 가능한 경우 인터페이스에 대한 포인터를 반환하는 자유 함수를 정의합니다.하지만 장면 뒤에는 복잡성이 있습니다.

클래스의 최종 사용자가해야 할 사항에 대해 디자인을 생각해야합니다.

5

상황에 따라 공유 포인터가 아무 것도 추가하지 않는 경우 일 수 있습니다. 평생 관리가 문제가 될 때마다 사용해야합니다. 자식 개체의 수명이 부모 개체의 수명보다 짧을 경우 원시 포인터를 사용할 때 문제가 발생하지 않습니다. 예를 들어, 부모가 자식 객체를 만들고 삭제하는 경우 (아무도 수행하지 않는 경우), 자식 객체를 삭제해야하는 대상은 없습니다.

+4

그리고 스마트 포인터에 부모 포인터를두면 아이는 부모의 일생을 지배하고 있습니다. 그것은 단지 기다리고있는 버그 일뿐입니다. 마지막 아이를 지우고 * poof! * 당신은 깔개를 당신 밑에서 뽑아냅니다. –

3

KeithB 내가 확장하고자하는 정말 좋은 지적이있다 (질문과 관련되지 않은 의미를하지만 주석에 맞지 않는)의 관계의 구체적인 경우

을 서브 객체를 가진 객체는 수명이 보장됩니다 : 부모 객체는 항상 자식 객체보다 오래 지속됩니다. 이 경우 자식 (구성원) 개체 이 부모 (포함) 개체의 소유권을 공유하지 않으며 shared_ptr은 사용하지 않아야합니다. 의미 론적 이유 (공유 소유권 없음) 또는 실용적인 이유로 사용하면 안됩니다. 메모리 누수 및 잘못된 삭제 등 모든 종류의 문제를 일으킬 수 있습니다.

설명을 쉽게하기 위해 부모 개체를 나타내는 데 P을 사용하고 하위 개체 또는 포함 된 개체를 참조하려면 C을 사용합니다. P 수명이 외부 적으로 처리되는 shared_ptr

경우, 사이클을 생성시키는 효과를 가질 것이다 P을 참조 C 다른 shared_ptr 추가. 참조 카운팅을 통해 메모리를 관리하면 대부분 메모리 누수가 발생합니다. P을 참조하는 마지막 외부 shared_ptr이 범위를 벗어나면 C의 포인터가 여전히 살아 있기 때문에 P의 참조 카운트에 도달하지 않습니다 0이고 더 이상 액세스 할 수없는 경우에도 개체가 해제되지 않습니다.

P이 다른 포인터에 의해 처리되면 포인터가 삭제되면 P 소멸자가 호출되어 C 소멸자가 호출됩니다. 에있는 P의 참조 카운트는 C이며 0이면 두 번 삭제됩니다.

가 소멸자 호출 될 때 P는 자동 저장 기간이 있으면 (대상 범위를 벗어나거나 함유 객체 소멸자가 호출된다) 다음 shared_ptr 새로운 혼성 아니었다 메모리 블록의 삭제를 실행한다.

일반적인 해결 방법은 weak_ptr 초를 사용하여 차단주기가되므로 자식 개체가 shared_ptr이 아닌 weak_ptr 인 채로 유지됩니다. 이 단계에서 문제는 동일합니다. weak_ptr을 만들려면 개체가 이미 건설 중에 발생할 수없는 shared_ptr에 의해 관리되어야합니다.

포인터를 통해 리소스 소유권을 처리하는 것은 안전하지 않지만 여기서는 소유권이 외부 적으로 처리되므로 문제가되지 않습니다.) 또는 참조 (다른 프로그래머에게 참조 된 객체 P은 참조 객체보다 남아 있습니다. C)

+0

클래스를 특정 스마트 포인터와 연결하는 아이디어가 마음에 들지 않습니다. C++ 0x를 사용하면'shared_ptr' 사용법의 절반을'unique_ptr'으로 대체 할 수 있습니다. (나는 아무것도 공유하지 않으려 고 포인터를 컨테이너에 넣기를 원했습니다). – UncleBens

+0

나는 내가 * 모든 것을 공유하기를 원하지 않는 유일한 * 이기심 *이라고 생각했다. 의미 론적 관점에서 볼 때 표준화되지는 않을지라도 범위 포인터를 좋아합니다 ... –

+0

'shared_ptr'에 대한 내용은 코드 절연 속성입니다 : shared_ptr '을 다음과 같이 조작 할 수 있습니다. 간단한'class Foo;'선언을 선언하고 아직 메모리를 올바르게 풀어줍니다. 다른 포인터 중 아무 것도 그것을 가지고 있지 않습니다 (예, 약간의 작업이 필요합니다). –

0

이 경우 실제로 shared_ptr을 사용해야합니까? 아이에게 포인터가있을 수 있습니까? 결국 그것은 자식 객체이므로 부모에 의해 소유되므로 부모에 대한 일반적인 포인터를 가질 수 없습니까?

+0

'Child'가 다른 엔티티 (사본)로 전달 될 수 있는지에 따라 달라질 것이라고 생각합니다. –

관련 문제