shared_ptr을 사용하는 예?


82

안녕하세요 저는 오늘 동일한 벡터 배열에 다른 유형의 객체를 삽입하는 방법에 대한 질문을했는데 그 질문의 코드는

 gate* G[1000];
G[0] = new ANDgate() ;
G[1] = new ORgate;
//gate is a class inherited by ANDgate and ORgate classes
class gate
{
 .....
 ......
 virtual void Run()
   {   //A virtual function
   }
};
class ANDgate :public gate 
  {.....
   .......
   void Run()
   {
    //AND version of Run
   }  

};
 class ORgate :public gate 
  {.....
   .......
   void Run()
   {
    //OR version of Run
   }  

};      
//Running the simulator using overloading concept
 for(...;...;..)
 {
  G[i]->Run() ;  //will run perfectly the right Run for the right Gate type
 } 

벡터를 사용하고 싶었 기 때문에 누군가가 그렇게해야한다고 썼습니다.

std::vector<gate*> G;
G.push_back(new ANDgate); 
G.push_back(new ORgate);
for(unsigned i=0;i<G.size();++i)
{
  G[i]->Run();
}

하지만 그와 다른 많은 사람들이 Boost 포인터 컨테이너
또는 shared_ptr. 나는 지난 3 시간 동안이 주제에 대해 읽었지만 문서는 나에게 꽤 진보 된 것 같다. **** 누구나 나에게 사용에 대한 작은 코드 예제 shared_ptrshared_ptr. ptr_vector, ptr_listptr_deque** ** 와 같은 다른 유형도 있습니다.

Edit1 : 다음을 포함하는 코드 예제도 읽었습니다.

typedef boost::shared_ptr<Foo> FooPtr;
.......
int main()
{
  std::vector<FooPtr>         foo_vector;
........
FooPtr foo_ptr( new Foo( 2 ) );
  foo_vector.push_back( foo_ptr );
...........
}

그리고 구문을 이해하지 못합니다!


2
어떤 구문을 이해하지 못합니까? 의 첫 번째 줄은 main라는 유형에 대한 공유 포인터를 포함 할 수있는 벡터를 만듭니다 Foo. 두 번째는 Foousing을 만들고 new이를 관리하기위한 공유 포인터를 만듭니다 . 세 번째는 공유 포인터의 복사본을 벡터에 넣습니다.
Mike Seymour

답변:


116

vectorof를 사용 shared_ptr하면 벡터를 걷고 delete각 요소를 호출하는 것을 잊었 기 때문에 메모리 누수 가능성 이 제거 됩니다. 예제의 약간 수정 된 버전을 한 줄씩 살펴 보겠습니다.

typedef boost::shared_ptr<gate> gate_ptr;

공유 포인터 유형에 대한 별칭을 만듭니다. 이렇게하면 std::vector<boost::shared_ptr<gate> >닫는 보다 큼 기호 사이의 공백 을 입력 하고 잊어 버리는 결과로 발생하는 C ++ 언어의 추악함을 방지 할 수 있습니다 .

    std::vector<gate_ptr> vec;

boost::shared_ptr<gate>객체로 구성된 빈 벡터를 만듭니다 .

    gate_ptr ptr(new ANDgate);

ANDgate인스턴스를 할당하고 shared_ptr. 이 작업을 별도로 수행하는 이유는 작업이 throw 될 때 발생할 수있는 문제를 방지하기위한 것입니다. 이 예에서는 불가능합니다. 부스트는 shared_ptr"모범 사례" 그것이 이유를 설명하는 가장 좋은 방법 대신 임시의 독립 개체로 할당 할 수는.

    vec.push_back(ptr);

이렇게하면 벡터에 새 공유 포인터가 만들어지고 여기에 복사 ptr됩니다. 의 내부에있는 참조 계수는 shared_ptr내부에 할당 된 객체 ptr가 안전하게 벡터로 전송되도록합니다.

설명되지 않은 것은 소멸자 shared_ptr<gate>가 할당 된 메모리가 삭제되었는지 확인한다는 것입니다. 이것은 메모리 누수가 방지되는 곳입니다. 에 대한 소멸자 는 벡터에 저장된 모든 요소에 대해 std::vector<T>소멸자 T가 호출 되도록합니다 . 그러나, 포인터의 소멸자 (예 gate*) 당신이 할당했다고 메모리를 삭제하지 않습니다 . 즉, 사용하여 피하려고하는 것입니다 shared_ptrptr_vector.


1
상세했습니다 :). 내 질문은 코드의 세 번째 줄에 관한 것입니다. gate_ptr ptr (new ANDgate); 그것은 나에게 꽤 익숙하지 않은 느낌이 들었습니다. ptr 유형의 공유 포인터 게이트와 중괄호 사이에 새로운 ANDgate를 보냈습니다! 그것은 혼란 스럽습니다.
Ahmed

6
@Ahmed : 전체 표현식은 변수 초기화 입니다. 값 5 int x(5);로 초기화 x하도록 작성할 수 있습니다 .이 경우에는 다음을 생성하는 new-expression의 값으로 초기화됩니다 ANDgate. new-expression의 값은 새 개체에 대한 포인터입니다.
Mike Seymour

42

나는에 대한 중요한 것들 중 하나 추가 할 것이다 shared_ptr유일의 것입니다 ' 지금 다음 구문을 구성을 :

shared_ptr<Type>(new Type(...));

이렇게하면에 대한 "실제"포인터 Type는 범위에 대해 익명이되고 공유 포인터에 의해서만 유지 됩니다 . 따라서이 "실제"포인터를 실수로 사용하는 것은 불가능합니다. 즉, 절대로 이렇게하지 마십시오.

Type* t_ptr = new Type(...);
shared_ptr<Type> t_sptr ptrT(t_ptr);
//t_ptr is still hanging around!  Don't use it!

이 방법이 작동하지만 이제 함수에 공유 포인터 외부에 있는 Type*포인터 ( t_ptr)가 있습니다. t_ptr언제 어디서나 사용하는 것은 위험합니다 . 왜냐하면 그것을 보유하고있는 공유 포인터가 언제 그것을 파괴할지 ​​모르기 때문입니다.

다른 클래스에서 반환 한 포인터도 마찬가지입니다. 작성하지 않은 클래스가 포인터를 건네면 일반적으로 shared_ptr. 클래스가 더 이상 해당 객체를 사용하고 있지 않다는 것을 확신 하지 않는 한 아닙니다 . 왜냐하면 당신이 shared_ptr그것을.


8
Ken이 말한 모든 것은 훌륭하고 사실입니다. 그러나 저는 지금 그것을 부르는 선호하는 방법은 단순히 그 형식이 더 효율적이기 때문에 auto t_ptr = make_shared<Type>(...);또는 동등 shared_ptr<Type> t_ptr = make_shared<Type>(...);하게 라고 믿습니다 .
Jason Sydes 2013 년

@KenSimon, 쉼표가있을 예정이다 ,사이 t_sptrptrT의를 shared_ptr<Type> t_sptr ptrT(t_ptr);?
Allanqunzi

예제 코드의 모호함을 제외하고, 좋은 경고입니다. 그러나 첫 번째 형식이 훨씬 더 깨끗하고 아마도 더 중요한 것은 분명히 스마트 포인터를 사용하는 사람이라면 누구나 위험한 원시를 피하기 위해 정확히 존재한다는 것을 알고 있기 때문입니다. 주위에 떠 다니는 포인터. 마지막 단락은 흥미 롭습니다. 고맙게도 나는 아직 원시 또는 불명확 한 유형의 포인트를 사용하도록 강요하는 라이브러리를 사용하지 않았지만 언젠가는 일어날 것이라고 확신합니다.
underscore_d

20

스마트 포인터 사용법을 배우는 것은 유능한 C ++ 프로그래머가되기위한 가장 중요한 단계 중 하나라고 생각합니다. 아시다시피 객체를 새로 만들 때마다 삭제하고 싶습니다.

발생하는 한 가지 문제는 예외가있는 경우 가능한 모든 실행 경로에서 개체가 항상 한 번만 해제되도록하는 것이 매우 어려울 수 있다는 것입니다.

이것이 RAII의 이유입니다 : http://en.wikipedia.org/wiki/RAII

객체가 모든 실행 경로에서 항상 한 번 삭제되도록하는 목적으로 도우미 클래스를 만듭니다.

이와 같은 클래스의 예는 다음과 같습니다. std :: auto_ptr

그러나 때로는 다른 사람과 물건을 공유하고 싶을 때가 있습니다. 더 이상 사용하지 않는 경우에만 삭제해야합니다.

참조 계산 전략이 개발되었지만 여전히 addref를 기억하고 ref를 수동으로 해제해야합니다. 본질적으로 이것은 신규 / 삭제와 동일한 문제입니다.

이것이 boost가 boost :: shared_ptr을 개발 한 이유입니다. 스마트 포인터를 참조하여 객체를 공유하고 의도 치 않게 메모리를 누출하지 않도록합니다.

C ++ tr1이 추가됨에 따라 이제 C ++ 표준에도 추가되지만 이름은 std :: tr1 :: shared_ptr <>입니다.

가능하면 표준 공유 포인터를 사용하는 것이 좋습니다. ptr_list, ptr_dequeue 등은 포인터 유형에 대한 IIRC 특수 컨테이너입니다. 지금은 무시합니다.

따라서 우리는 귀하의 예에서 시작할 수 있습니다.

std::vector<gate*> G; 
G.push_back(new ANDgate);  
G.push_back(new ORgate); 
for(unsigned i=0;i<G.size();++i) 
{ 
  G[i]->Run(); 
} 

여기서 문제는 G가 범위를 벗어날 때마다 G에 추가 된 2 개의 객체가 누출된다는 것입니다. std :: tr1 :: shared_ptr을 사용하도록 다시 작성해 보겠습니다.

// Remember to include <memory> for shared_ptr
// First do an alias for std::tr1::shared_ptr<gate> so we don't have to 
// type that in every place. Call it gate_ptr. This is what typedef does.
typedef std::tr1::shared_ptr<gate> gate_ptr;    
// gate_ptr is now our "smart" pointer. So let's make a vector out of it.
std::vector<gate_ptr> G; 
// these smart_ptrs can't be implicitly created from gate* we have to be explicit about it
// gate_ptr (new ANDgate), it's a good thing:
G.push_back(gate_ptr (new ANDgate));  
G.push_back(gate_ptr (new ORgate)); 
for(unsigned i=0;i<G.size();++i) 
{ 
   G[i]->Run(); 
} 

G가 범위를 벗어나면 메모리가 자동으로 회수됩니다.

우리 팀의 신입생들을 괴롭히는 연습으로 그들 자신의 스마트 포인터 클래스를 작성하도록 요청했습니다. 그런 다음 완료되면 즉시 클래스를 버리고 다시 사용하지 마십시오. 스마트 포인터가 내부에서 어떻게 작동하는지에 대한 중요한 지식을 얻었기를 바랍니다. 정말 마법은 없습니다.


저의 강사가 저의 수업을 작성하는 것에 대해 비슷한 조언을 해주었으므로 확실히 해보겠습니다. TY.
Ahmed

모든 게이트를 실행하려면 반복기를 사용해야합니다.for( auto itt = G.begin(); itt != G.end(); ++itt ){ itt->Run(); }
Guillaume Massé 2013

1
또는 C ++의 새로운 "foreach"
또 다른 메타

2

boost 문서는 꽤 좋은 시작 예제를 제공합니다. shared_ptr 예제 (실제로는 스마트 포인터의 벡터에 관한 것입니다) 또는 shared_ptr doc Johannes Schaub의 다음 답변은 boost 스마트 포인터를 꽤 잘 설명 합니다. 스마트 포인터 설명

(가능한 한 적은 단어로) ptr_vector의 개념은 저장된 포인터 뒤에있는 메모리 할당 해제를 처리한다는 것입니다. 예제에서와 같이 포인터 벡터가 있다고 가정 해 보겠습니다. 응용 프로그램을 종료하거나 벡터가 정의 된 범위를 벗어날 때 스스로 정리해야하지만 (동적으로 ANDgate와 ORgate를 할당했습니다) 벡터가 포인터를 저장하기 때문에 벡터를 지우는 것만으로는 수행되지 않습니다. 실제 개체가 아닙니다 (파괴되지 않지만 포함 된 내용).

 // if you just do
 G.clear() // will clear the vector but you'll be left with 2 memory leaks
 ...
// to properly clean the vector and the objects behind it
for (std::vector<gate*>::iterator it = G.begin(); it != G.end(); it++)
{
  delete (*it);
}

boost :: ptr_vector <>는 위의 작업을 대신 처리합니다. 즉, 저장하는 포인터 뒤에있는 메모리 할당을 해제합니다.


shared_ptr은 스마트 포인터입니다. 포인터 유형에 AI를 추가한다고 가정 해 보겠습니다. 일반 포인터를위한 반짝이는 "래퍼"입니다. ptr_vector는 포인터에 대한 스마트 컨테이너입니다. 포인터 컨테이너에 대한 "래퍼"입니다.
celavek

그래서 ptr_vector는 일반 벡터에 대한 일종의 대체입니까?
Ahmed

@Ahmed 나는 당신이 그렇게 생각할 수 있다고 생각합니다.
celavek

2

Boost를 통해 할 수 있습니다>

std::vector<boost::any> vecobj;
    boost::shared_ptr<string> sharedString1(new string("abcdxyz!"));    
    boost::shared_ptr<int> sharedint1(new int(10));
    vecobj.push_back(sharedString1);
    vecobj.push_back(sharedint1);

> 벡터 컨테이너에 다른 개체 유형을 삽입합니다. 액세스하려면 dynamic_cast처럼 작동하는 any_cast를 사용해야합니다. 필요에 따라 작동하기를 바랍니다.


1
#include <memory>
#include <iostream>

class SharedMemory {
    public: 
        SharedMemory(int* x):_capture(x){}
        int* get() { return (_capture.get()); }
    protected:
        std::shared_ptr<int> _capture;
};

int main(int , char**){
    SharedMemory *_obj1= new SharedMemory(new int(10));
    SharedMemory *_obj2 = new SharedMemory(*_obj1);
    std::cout << " _obj1: " << *_obj1->get() << " _obj2: " << *_obj2->get()
    << std::endl;
    delete _obj2;

    std::cout << " _obj1: " << *_obj1->get() << std::endl;
    delete _obj1;
    std::cout << " done " << std::endl;
}

이것은 shared_ptr 작동의 예입니다. _obj2가 삭제되었지만 포인터는 여전히 유효합니다. 출력은 ./test _obj1 : 10 _obj2 : 10 _obj2 : 10 done


0

동일한 컨테이너에 다른 객체를 추가하는 가장 좋은 방법은 make_shared, 벡터 및 범위 기반 루프를 사용하는 것입니다. 그러면 멋지고 깔끔하며 "읽을 수있는"코드를 갖게됩니다!

typedef std::shared_ptr<gate> Ptr   
vector<Ptr> myConatiner; 
auto andGate = std::make_shared<ANDgate>();
myConatiner.push_back(andGate );
auto orGate= std::make_shared<ORgate>();
myConatiner.push_back(orGate);

for (auto& element : myConatiner)
    element->run();
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.