2012-02-27 2 views
15

C++ 11에서는 boost :: ptr_containers가 C++ 11로 대체되었는지 직접 확인했습니다. 예를 들어 a std::vector<std::unique_ptr<T> >,하지만 이것이 전체 교체인지 확실하지 않습니다. 이러한 사례를 처리하는 데 권장되는 방법은 무엇입니까?std :: unique_ptr 대 boost :: ptr_container가있는 stl 컨테이너

+2

'unique_ptr'을 사용하면 여전히 노드를 역 참조해야하지만 그 외의 경우에는 거의 동일하게 동작해야합니다. –

답변

19

그들은 두 가지 비슷하지만 다른 문제를 실제로 해결합니다.

포인터 컨테이너는 값이 아니라 할당 된 메모리를 가리키는 포인터처럼 컨테이너에 객체를 저장하는 방법입니다. 그들은 에 대한 힘을 다하여 포인터를 저장한다는 사실을 숨 깁니다. 즉,

  • 컨테이너의 항목은 NULL 일 수 없습니다.
  • 이터레이터와 함수에서 얻은 값은 입니다. 유형에 대한 포인터가 아닌입니다.
  • 많은 표준 알고리즘을 사용하여 작업 할 수 있습니다 ... 까다 롭습니다. 그리고 "까다로운"것은 부러진 것을 의미합니다. 포인터 컨테이너에는 자체 알고리즘이 내장되어 있습니다.

그러나, 포인터의 컨테이너 가 포인터의 컨테이너를하고 있다는을 알고 있다는 사실, 그들은 몇 가지 새로운 기능을 제공 할 수 있습니다 :

  • 사용을 통해, 깊은 복사를 수행하는 clone 멤버 함수 개체 유형에 대한 특정 "복제 가능"개념
  • 컨테이너가 개체의 소유권을 해제 할 수있는 기능 (예 : 얕은 복사본 후에).
  • 소유권을 다른 컨테이너로 전송하는 기본 제공 함수.

그들은 실제로 아주 다른 개념입니다. 포인터 컨테이너가 특수 기능을 사용하여 자동으로 수행 할 수있는 많은 작업이 수동으로 수행되어야합니다.

포인터가 인 컨테이너가 필요하면 unique_ptr 컨테이너를 사용할 수 있습니다. 하지만 할당량을 쌓아 올린 물건을 저장해야하고 소유권과 관련된 특수 게임을하고 싶다면 포인터 컨테이너는 나쁜 생각이 아닙니다.

+1

다른 하나는 다형 객체를위한 컨테이너 인 반면, 그 중 하나는 명시 적으로 포인터의 컨테이너라고 말할 수 있습니다 ... – Mehrdad

22

몇 가지 다형성 객체를 (힙에 대한 포인터로) 컨테이너에 넣은 다음 그 컨테이너를 std :: 알고리즘과 함께 사용하는 간단한 프로그램을 작성하기로 결정했습니다. 예를 들어 std::remove_if을 선택했습니다. 여기

내가 vector<unique_ptr<T>>으로 어떻게 할 것입니다 :

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

class Animal 
{ 
public: 
    Animal() = default; 
    Animal(const Animal&) = delete; 
    Animal& operator=(const Animal&) = delete; 
    virtual ~Animal() = default; 

    virtual void speak() const = 0; 
}; 

class Cat 
    : public Animal 
{ 
public: 
    virtual void speak() const {std::cout << "Meow\n";} 
    virtual ~Cat() {std::cout << "destruct Cat\n";} 
}; 

class Dog 
    : public Animal 
{ 
public: 
    virtual void speak() const {std::cout << "Bark\n";} 
    virtual ~Dog() {std::cout << "destruct Dog\n";} 
}; 

class Sheep 
    : public Animal 
{ 
public: 
    virtual void speak() const {std::cout << "Baa\n";} 
    virtual ~Sheep() {std::cout << "destruct Sheep\n";} 
}; 

int main() 
{ 
    typedef std::unique_ptr<Animal> Ptr; 
    std::vector<Ptr> v; 
    v.push_back(Ptr(new Cat)); 
    v.push_back(Ptr(new Sheep)); 
    v.push_back(Ptr(new Dog)); 
    v.push_back(Ptr(new Sheep)); 
    v.push_back(Ptr(new Cat)); 
    v.push_back(Ptr(new Dog)); 
    for (auto const& p : v) 
     p->speak(); 
    std::cout << "Remove all sheep\n"; 
    v.erase(
     std::remove_if(v.begin(), v.end(), 
         [](Ptr& p) 
          {return dynamic_cast<Sheep*>(p.get());}), 
     v.end()); 
    for (auto const& p : v) 
     p->speak(); 
} 

이 출력 : 나에게 좋아 보인다

Meow 
Baa 
Bark 
Baa 
Meow 
Bark 
Remove all sheep 
destruct Sheep 
destruct Sheep 
Meow 
Bark 
Meow 
Bark 
destruct Dog 
destruct Cat 
destruct Dog 
destruct Cat 

.

boost::ptr_vector<Animal> v; 
v.push_back(new Cat); 
v.push_back(new Sheep); 
v.push_back(new Dog); 
v.push_back(new Sheep); 
v.push_back(new Cat); 
v.push_back(new Dog); 
for (auto const& p : v) 
    p.speak(); 
std::cout << "Remove all sheep\n"; 
v.erase(
    std::remove_if(v.begin(), v.end(), 
        [](Animal& p) 
         {return dynamic_cast<Sheep*>(&p);}), 
    v.end()); 
for (auto const& p : v) 
    p.speak(); 

algorithm:1897:26: error: overload resolution selected deleted operator '=' 
       *__first = _VSTD::move(*__i); 
       ~~~~~~~~^~~~~~~~~~~~~~~~~~ 
test.cpp:75:9: note: in instantiation of function template specialization 'std::__1::remove_if<boost::void_ptr_iterator<std::__1::__wrap_iter<void 
     **>, Animal>, Sheep *(^)(Animal &)>' requested here 
     std::remove_if(v.begin(), v.end(), 
     ^
test.cpp:12:13: note: candidate function has been explicitly deleted 
    Animal& operator=(const Animal&) = delete; 
      ^
1 error generated. 

문제는 boost::ptr_vector의 기능입니다 : 그러나 나는 문제 ptr_vector이 번역을 볼 수있는 반복자는 내부에 저장된 포인터를 반환하지 않습니다. 역 참조 된 포인터를 반환합니다.따라서 컨테이너가 std::algorithms과 함께 사용될 때 알고리즘은 저장된 포인터 대신 객체에 저장된 객체를 복사하려고 시도합니다. 지금이 잘못된 결과

class Animal 
{ 
public: 
    Animal() = default; 
    virtual ~Animal() = default; 

    virtual void speak() const = 0; 
}; 

: 하나의 실수가 아닌 복사 가능한 당신의 다형성 객체를 만들기 위해 잊어 버린 경우

후 대신 컴파일 타임 오류의 런타임 오류의 결과로 의미가 자동으로 제공되는 복사 출력 :

Meow 
Baa 
Bark 
Baa 
Meow 
Bark 
Remove all sheep 
destruct Cat 
destruct Dog 
Meow 
Baa 
Bark 
Baa 
destruct Cat 
destruct Sheep 
destruct Dog 
destruct Sheep 

이 런타임 오류는 vector<unique_ptr>을 사용할 때 발생할 수 없습니다.

포인터의 컨테이너를 저장하지만 참조 컨테이너를 제시하는 임피던스 불일치는 일반적인 알고리즘을 사용하여 컨테이너를 안전하게 사용하는 것과는 정반대로 나타납니다. 실제로 ptr_containers에는 많은 알고리즘의 사용자 지정 버전이 제공됩니다.

v.erase_if([](Animal& p) 
       {return dynamic_cast<Sheep*>(&p);}); 

당신이 ptr_containers의 구성원으로 공급하지 않는 돌연변이 시퀀스 알고리즘을 필요로하는 경우는, 그 도달 유혹하지 마십시오 ptr_containers으로이 작업을 수행하는 올바른 방법은 그 멤버 알고리즘을 사용하는 것입니다 <algorithm> 또는 다른 제 3자가 제공 한 일반 알고리즘.

요약하면 boost :: ptr_containers는 유일한 실용적인 옵션이 std::vector<boost::shared_ptr<T>> 인 경우 실제 필요를 채웠습니다. 그러나 이제 std::vector<std::unique_ptr<T>>을 사용하면 오버 헤드 인수가 없어집니다. C++ 11 솔루션에는 안전성과 유연성면에서 모두 장점이 있습니다. "클론 의미론"이 필요하다면, 나는 심각하게 자신의 clone_ptr<T>을 쓰고 표준 컨테이너와 알고리즘을 사용하는 것을 고려할 것이다.

std :: lib를 다시 사용하면 컨테이너의 옵션을 부스트 라이브러리보다 더 개방적으로 유지 (예 : unordered_set/map, forward_list) 할 수 있으며 std :: 알고리즘의 옵션을 최대한 넓게 유지할 수 있습니다.

이미 말했듯이, 이미 boost :: ptr_containers를 사용하여 디버깅 된 코드를 사용하고 있다면, 그것을 바꿀 필요가 없습니다.

+0

"오버 헤드 인수가 없어졌습니다"- 오늘, VS2013-Express로 몇 가지 테스트를 수행했습니다. 놀랍게도'vector '(/ O2 release build; boost 1.55)보다'ptr_vector '를 사용하면 더 나은 성능 결과를 얻을 수 있습니다. 이 의견은 명확한 YMMV와 함께 제공됩니다. 아직까지 파고 들지는 않았지만 성능면에서 매우 좁은 공간 인 경우 두 가지를 모두 살펴 보는 것이 흥미로울 수 있습니다. –

+0

재미있는 보고서에 감사드립니다. VS2013을 실험 할 필요가 없습니다. 'sizeof (unique_ptr ) == sizeof (T *)'입니까?/O2가 가장 높은 최적화 설정입니까? –

+0

예, sizeof (uq_ptr) == sizeof (T *) == 4./O2는 VS에서 최대 속도입니다. 나는 인덱스'[]'액세스에 대해 ptr_vector로 50 %의 속도 향상을 보았습니다. –

관련 문제