2012-01-03 15 views
47

다른 데이터 객체에서 채워진 새로운 Foobar 객체를 반환하는 정적 팩토리 메소드를 작성했습니다. 나는 최근에 소유권 의미론에 사로 잡혀 있었고이 팩토리 메소드가 unique_ptr을 돌려줌으로써 올바른 메시지를 전달하고 있는지 궁금합니다.소유권 의미론과 같은 원시 포인터에 대해 unique_ptr을 반환하는 나쁜 습관이 있습니까?

class Foobar { 
public: 
    static unique_ptr<Foobar> factory(DataObject data); 
} 

제 의도는 클라이언트 코드에 포인터를 소유하고 있다는 것입니다. 스마트 포인터가 없으면 단순히 Foobar*을 반환합니다. 그러나 잠재적 인 버그를 피하기 위해이 메모리를 삭제하도록 시행하고 싶습니다. 따라서 unique_ptr은 적절한 해결책으로 보입니다. 클라이언트가 포인터의 수명을 연장하려면 unique_ptr을 얻은 후 .release()을 호출하면됩니다.

Foobar* myFoo = Foobar::factory(data).release(); 

내 질문은 두 부분으로 제공 :

  1. 이 방법이 올바른 소유권 의미를 전달합니까?
  2. 원시 포인터 대신 unique_ptr을 반환하는 것은 "나쁜 습관"입니까?
+0

클라이언트에게 포인터를 소유하고 있음을 알리기 위해 unqiue_ptr을 반환합니까? 이는 정확하게 (내가 고유 한 포인터의 소유권을 명시 적으로 가져야하기 때문에) 예상했던 것의 반대입니다. – jknupp

+1

대신 C++ 11을 사용할 수있는 경우 move-semantics를 사용할 수 있습니다. 이를 통해 사용자는 공장에서 생성 된 객체의 수명을 연장하는 방법을 결정할 수 있습니다. – evnu

+1

@evnu 그건 당신을 위해 자동으로 이루어진 것입니다. –

답변

58

공장 방법에서 std::unique_ptr을 반환하는 것은 좋으며 권장되는 방법입니다. 전달하는 메시지는 다음과 같습니다 (IMO) : 이제이 개체의 유일한 소유자가되었습니다. 또한, 여러분의 편의를 위해, 객체는 스스로를 파괴하는 방법을 알고 있습니다.

이것은 원시 포인터 (클라이언트가이 포인터를 처리하는 방법과 기억해야 할 위치)를 반환하는 것이 훨씬 낫다고 생각합니다.

그러나 평생 연장하기 위해 포인터를 공개하는 것에 대한 귀하의 의견을 이해하지 못합니다. 일반적으로 나는 release을 smartpointer로 호출하는 어떤 이유도 볼 수 없다. 왜냐하면 포인터는 항상 RAII 구조에 의해 관리되어야한다고 생각하기 때문이다. (단지 release이라고하는 유일한 상황은 다른 관리 데이터 구조에 포인터를 두는 것이다. 추가 정리를 보증하기 위해 뭔가를 한 후에 다른 deleter가있는 unique_ptr).

따라서 클라이언트는 (그리고해야한다) 단순히 unique_ptr 곳을 저장할 수 있습니다 (예 : 된 움직임 반환 일로부터 구축 한 또 다른 unique_ptr 등)만큼 그들은 여러 복사본을 필요로하는 경우가 객체 (또는 shared_ptr을 필요로하는 포인터의).따라서 클라이언트 측 코드는 더 같이해야한다 : 나는 또한 (이 경우 std::unique_ptr<Foobar>에서) 반환 된 포인터 유형에 대한 typedef를 추가

개인적으로
std::unique_ptr<FooBar> myFoo = Foobar::factory(data); 
//or: 
std::shared_ptr<FooBar> myFoo = Foobar::factory(data); 

와 나 (이 경우 표준에 :: default_deleter)를 사용 Deleter가에 당신의 팩토리 객체. 나중에 포인터 할당을 변경하기로 결정한 경우 (따라서 포인터를 파기하는 다른 방법이 필요하며 두 번째 템플릿 매개 변수 std::unique_ptr으로 표시됨)이 더 쉽습니다. 이렇게하면 다음과 같이됩니다 :

class Foobar { 
public: 
    typedef std::default_deleter<Foobar>  deleter; 
    typedef std::unique_ptr<Foobar, deleter> unique_ptr; 

    static unique_ptr factory(DataObject data); 
} 

Foobar::unique_ptr myFoo = Foobar::factory(data); 
//or: 
std::shared_ptr<Foobar> myFoo = Foobar::factory(data); 
+3

'release()'에 대한 나의 언급이 오도되거나 혼란 스럽다는 것을 의미하지는 않았다. 미안하다. 이 새로운 코드는 스마트 포인터 (COM이 아닌)를 사용하지 않는 상당히 큰 기존 응용 프로그램 (C++의 1 ~ 2 백만 라인)에 포함될 예정입니다.레거시 코드가 효과적으로 "필요"하게되면 원시 포인터에 도달하는 합리적인 방법이 없다면 팀원들이 걱정할 것입니다. 물론 적절한 경고를 주며 가능한 경우 unique_ptr/shared_ptr을 사용하도록 권장합니다. 제 typedef 아이디어가 정말 마음에 드는데 제임스 맥 넬리 스 (James McNellis)와는 별개로 대답을 설정합니다. ' 통찰력에 감사드립니다. –

17

std::unique_ptr은 그것이 가리키는 대상을 고유하게 소유합니다. 그것은 "나는이 물건을 소유하고있다. 아무도하지 않는다."

정확하게 표현하려고하는 것입니다 : "이 기능을 호출 한 사람 : 이제는이 개체의 유일한 소유자이며, 원하는대로 그 일생을 책임집니다."라고 말하고있는 것입니다.

+0

사실, 왜'std :: shared_pointer <>'를 리턴하지 않을까요? 동일한 효과를 얻지 만 포인터의 여러 사본을 허용합니다. –

+8

@ AndréCaron : 소유주를 발신자에게 양도하려는 경우 'std :: shared_ptr'을 사용하는 이유는 무엇입니까? 원한다면 호출자가 객체의 소유권을'std :: shared_ptr' (또는 호출자가 원하는 경우 다른 종류의 스마트 포인터)로 보냅니다. –

+7

@ André Caron : 이유는 기본적으로 성능입니다. 팩토리는 클라이언트가'std :: unique_ptr'을 통해'std :: shared_ptr'에 의해 추가 된 기능을 할 것인지 여부를 알 수 없기 때문에, 어떤 기능에 대한 성능을 희생시키는 이유는 필요하지 않을 수 있습니다. 'shared_ptr'은'unique_ptr'에서 생성되고 할당 될 수 있기 때문에 실제로 이점이 없습니다. – Grizzly

6

정확하게 의미를 전달하며 C++의 모든 팩토리가 작동해야한다고 생각합니다. std::unique_ptr<T>은 소유권 의미를 부여하지 않으며 매우 저렴합니다.