C++ 11에서는 boost :: ptr_containers가 C++ 11로 대체되었는지 직접 확인했습니다. 예를 들어 a std::vector<std::unique_ptr<T> >
,하지만 이것이 전체 교체인지 확실하지 않습니다. 이러한 사례를 처리하는 데 권장되는 방법은 무엇입니까?std :: unique_ptr 대 boost :: ptr_container가있는 stl 컨테이너
답변
그들은 두 가지 비슷하지만 다른 문제를 실제로 해결합니다.
포인터 컨테이너는 값이 아니라 할당 된 메모리를 가리키는 포인터처럼 컨테이너에 객체를 저장하는 방법입니다. 그들은 에 대한 힘을 다하여 포인터를 저장한다는 사실을 숨 깁니다. 즉,
- 컨테이너의 항목은 NULL 일 수 없습니다.
- 이터레이터와 함수에서 얻은 값은 입니다. 유형에 대한 포인터가 아닌입니다.
- 많은 표준 알고리즘을 사용하여 작업 할 수 있습니다 ... 까다 롭습니다. 그리고 "까다로운"것은 부러진 것을 의미합니다. 포인터 컨테이너에는 자체 알고리즘이 내장되어 있습니다.
그러나, 포인터의 컨테이너 가 포인터의 컨테이너를하고 있다는을 알고 있다는 사실, 그들은 몇 가지 새로운 기능을 제공 할 수 있습니다 :
- 사용을 통해, 깊은 복사를 수행하는
clone
멤버 함수 개체 유형에 대한 특정 "복제 가능"개념 - 컨테이너가 개체의 소유권을 해제 할 수있는 기능 (예 : 얕은 복사본 후에).
- 소유권을 다른 컨테이너로 전송하는 기본 제공 함수.
그들은 실제로 아주 다른 개념입니다. 포인터 컨테이너가 특수 기능을 사용하여 자동으로 수행 할 수있는 많은 작업이 수동으로 수행되어야합니다.
포인터가 인 컨테이너가 필요하면 unique_ptr
컨테이너를 사용할 수 있습니다. 하지만 할당량을 쌓아 올린 물건을 저장해야하고 소유권과 관련된 특수 게임을하고 싶다면 포인터 컨테이너는 나쁜 생각이 아닙니다.
다른 하나는 다형 객체를위한 컨테이너 인 반면, 그 중 하나는 명시 적으로 포인터의 컨테이너라고 말할 수 있습니다 ... – Mehrdad
몇 가지 다형성 객체를 (힙에 대한 포인터로) 컨테이너에 넣은 다음 그 컨테이너를 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를 사용하여 디버깅 된 코드를 사용하고 있다면, 그것을 바꿀 필요가 없습니다.
"오버 헤드 인수가 없어졌습니다"- 오늘, VS2013-Express로 몇 가지 테스트를 수행했습니다. 놀랍게도'vector
재미있는 보고서에 감사드립니다. VS2013을 실험 할 필요가 없습니다. 'sizeof (unique_ptr
예, sizeof (uq_ptr) == sizeof (T *) == 4./O2는 VS에서 최대 속도입니다. 나는 인덱스'[]'액세스에 대해 ptr_vector로 50 %의 속도 향상을 보았습니다. –
'unique_ptr'을 사용하면 여전히 노드를 역 참조해야하지만 그 외의 경우에는 거의 동일하게 동작해야합니다. –