2012-01-08 5 views
2

가상 함수가있는 힙 할당 객체를 저장할 수있는 컬렉션이 필요합니다.참조 카운팅을 사용하는 C++ 포인터 컨테이너

boost::shared_ptr, std::unique_ptr (C++ 11) 및 boost::ptr_(vector|list|map)으로 알려져 있지만 중복 포인터 문제는 해결되지 않습니다. - _list 두 개의 포인터를 포함합니다

void SomeClass::add(T* ptr) 
{ 
    _list.push_back(ptr); 
} 

내가 같은 매개 변수를 두 번 add를 호출하지만 ptr가 : -

그냥 문제를 설명하기 위해 내가 힙 할당 포인터 및 향후 사용을 위해 저장을 허용하는 기능을 가지고 동일한 객체에 대해 _list이 파괴되면 동일한 객체가 여러 번 삭제됩니다.

그가 저장하고 삭제 시간에 사용하는 포인터를 계산하면이 문제는 해결되고 개체는 여러 번 삭제되지 않습니다.

그래서 질문은 :

누군가와 포인터의 컬렉션 일부 라이브러리 (벡터,리스트, 본질적으로지도)를 알고 있는가 자동 삭제 파괴와 참조 카운팅의 지원에?

아니면 다른 기술을 사용하여이 문제를 해결할 수 있습니까?

업데이트 :

나는 중복 포인터의 지원이 필요합니다. 그래서 std::set을 사용할 수 없습니다.

Kerrek SBGrizzly으로 언급 한 - 일반적으로 원시 포인터를 사용하는 나쁜 생각과 std::make_shared을 사용하고 new를 통해 인스턴스화 잊어 제안합니다. 그러나 이것은 클라이언트 측 코드의 책임입니다. 제가 설계 한 클래스가 아닙니다. 내가

다음
void SomeClass::add(std::shared_ptr<T> ptr) 
{ 
    _list.push_back(ptr); 
} 

누군가에게 add 서명 (물론 _list 용기)를 변경하더라도 (모르는 사람에 대한 std::make_shared) 여전히를 작성할 수 있습니다

SomeClass instance; 
T* ptr = new T(); 
instance.add(ptr); 
instance.add(ptr); 

을 그래서이 전체 아닙니다 솔루션을 기다리고 있지만 코드를 작성하는 경우에만 유용합니다.

업데이트 2 : 대안 용액

i는 (생성 된 복사 생성자 사용) clonning 알았다. - 통화 및 중복 포인터를 허용 (일부 기본 클래스 (인터페이스) 확장 클래스 R)

template <typename R> 
void SomeClass::add(const R& ref) 
{ 
    _list.push_back(new R(ref)); 
} 

이 가상 메서드를 허용 할 것이다 : 나는이처럼 내 add 기능을 변경할 수 있음을 의미한다. 그러나이 솔루션에는 복제에 대한 오버 헤드가 있습니다.

+0

공유 포인터 저장 방법은 어떻습니까? 파괴되면 다른 하나는 여전히 유효합니다. 두 개체가 모두 파괴되었을 때만 (그리고 같은 개체에 다른 공유 포인터가 존재하지 않는 경우에만) 포인터가 가리키는 개체는 파괴됩니다. – cHao

+1

두 개의'shared_ptr'을 같은 객체에 하나만 삭제하면 그 객체는 한번 파괴됩니다. 문제가 무엇인지 모르겠습니다. –

+0

@BenVoigt : 두 개의 'shared_ptrs'중 하나가 다른 하나를 복사하여 생성 된 경우에만 작동합니다. 원시 포인터에서 두 가지를 모두 만들지는 않습니다. 추가 할 작업은 다음과 같습니다. – Grizzly

답변

1

예 : std::list<std::shared_ptr<T>>.

공유 포인터는 <memory> 또는 이전 플랫폼의 <tr1/memory> 또는 Boost의 <boost/shared_ptr.hpp>에서 사용할 수 있습니다. 공유 포인터가 직접 처리하므로 아무 것도 수동으로 삭제할 필요가 없습니다. 당신은 그러나 처음부터 공유 포인터 내부 모든 힙 포인터를 유지해야합니다 같은 객체에 다른 공유 포인터, 공유 포인터의 복사본을 만들 당신이 경우

std::shared_ptr<T> p(new T); // legacy 
auto p = std::make_shared<T>(); // better 

(보다는를 구성 기본 원시 포인터에서 새 공유 포인터) : auto q = p;


여기에서의 도덕적 : 당신이 알몸으로 포인터를 사용하는 경우, 뭔가 잘못된 것입니다.

+0

그는 두 개의'shared_ptr'을 두 번 삭제하는 동일한 포인터로 구성한다고합니다. –

+0

@SethCarnegie : 메모가 추가되었습니다! –

1

스마트 컨테이너는 기본 컨테이너를 비교하여 스마트 포인터가 비교된다는 것을 알 수 있습니다. 따라서 원하는 스마트 폰을 std::set으로 사용할 수 있습니다. 개인적으로는 shared_ptr을 통해 std::unique_ptr을 사용합니다. 소유권을 훨씬 명확하게하고 (소유자가 누구든지 보유하고있는 사람이면 누구나) 소유권을 훨씬 덜 갖기 때문에 멀리 떨어져있을 수 있습니다. 나는 이것이 거의 모든 코드에 충분하다는 것을 알았다. 코드는 같은 것을 보일 것이다 다음

std::set<std::unique_ptr<T> > _list; 

void SomeClass::add(T* ptr) 
{ 
    std::unique_ptr<T> p(ptr); 
    auto iter = _list.find(p); 
    if(iter == _list.end()) 
    _list.insert(std::move(p)); 
    else 
    p.release(); 
} 

내가 지금 그 잔인한 있는지 확실하지 않습니다 (삽입이 실패 할 경우 insert이, 아무것도하지 않는 것이 보증되어 있는지 확인해야합니다)하지만 작동합니다 . shared_ptr<T>을 사용하면 비슷한 모양이되지만 relase 회원이 없어서 조금 더 복잡해집니다. 이 경우 나는 아마도 shared_ptr<T>을 생성 할 것이고, 아무 것도하지 않고 deleter도 찾을 호출에 전달한 다음 실제로 삽입 된 shared_ptr<T>을 추가로 호출 할 것입니다.

물론 개인적으로 나는 포인터의 소유권이 바뀌면 스마트 포인터 주위를 항상 지나치게 지나치지 않습니다. 따라서 SomeClass::addvoid SomeClass::add(std::unique_ptr<T> ptr) 또는 void SomeClass::add(std::shared_ptr<T> ptr)으로 다시 작성하여 어쨌든 여러 인스턴스가있는 문제를 해결할 수 있습니다 (포인터가 항상 랩핑되어있는 한).

+0

이것은 사용자에게이 함수가 포인터의 소유권을 가끔 갖기 때문에 때때로 위험합니다. –

+0

@Kerrek SB : 나는 그것을 깨닫는다. 그러나 나는 그것을 보았을 때 그 질문이 요구하는 것이었다. 그러나 당신이 저를 생각 나게하는 것을 아십시오, 나는 나쁜 생각 인 까 왜 주를 추가하고 싶었다. – Grizzly