문제

사용할 때 발에 자신을 쏠 수있는 몇 가지 방법은 무엇입니까? boost::shared_ptr? 다시 말해, 내가 사용할 때 어떤 함정을 피해야 하는가 boost::shared_ptr?

도움이 되었습니까?

해결책

주기적 참조 : a shared_ptr<> a shared_ptr<> 원래 개체에. 당신이 사용할 수있는 weak_ptr<> 물론이주기를 깨기 위해.


나는 다음을 의견에서 내가 말하는 것에 대한 예로 다음을 추가합니다.

class node : public enable_shared_from_this<node> {
public :
    void set_parent(shared_ptr<node> parent) { parent_ = parent; }
    void add_child(shared_ptr<node> child) {
        children_.push_back(child);
        child->set_parent(shared_from_this());
    }

    void frob() {
        do_frob();
        if (parent_) parent_->frob();
    }

private :
    void do_frob();
    shared_ptr<node> parent_;
    vector< shared_ptr<node> > children_;
};

이 예에는 노드 트리가 있으며 각각은 부모에 대한 포인터가 있습니다. FROB () 멤버 함수는 어떤 이유로 든 나무를 통해 위쪽으로 파문됩니다. (이것은 전적으로 외설적이지는 않습니다. 일부 GUI 프레임 워크가 이런 식으로 작동합니다).

문제는 최상위 노드에 대한 참조를 잃어 버리면 최상위 노드는 여전히 아이들에 대한 강한 참조를 가지고 있으며 모든 아이들도 부모에 대한 강한 언급을 가지고 있다는 것입니다. 이것은 모든 인스턴스가 스스로 청소하는 것을 막는 원형 참조가 있음을 의미하지만, 실제로 코드에서 트리에 도달하는 방법은 없지만이 메모리는 누출됩니다.

class node : public enable_shared_from_this<node> {
public :
    void set_parent(shared_ptr<node> parent) { parent_ = parent; }
    void add_child(shared_ptr<node> child) {
        children_.push_back(child);
        child->set_parent(shared_from_this());
    }

    void frob() {
        do_frob();
        shared_ptr<node> parent = parent_.lock(); // Note: parent_.lock()
        if (parent) parent->frob();
    }

private :
    void do_frob();
    weak_ptr<node> parent_; // Note: now a weak_ptr<>
    vector< shared_ptr<node> > children_;
};

여기서, 부모 노드는 약한 포인터로 대체되었습니다. 더 이상 언급 된 노드의 수명에 더 이상 말하지 않습니다. 따라서, 가장 상단 노드가 이전 예에서와 같이 범위를 벗어나면 자녀에 대한 강한 언급을 가지고 있지만, 그 아이들은 부모에 대한 강한 언급을 가지고 있지 않습니다. 따라서 대상에 대한 강한 언급은 없으며 스스로를 정리합니다. 결과적으로, 이것은 아이들이 하나의 강한 참조를 잃게하여 청소 등을 만듭니다. 요컨대, 이것은 누출되지 않습니다. 그리고 shared_ptr <>을 약한 _ptr <>으로 전략적으로 교체함으로써.

참고 : 위는 std :: shared_ptr <> 및 std :: excim_ptr <>에 동일하게 적용됩니다. boost :: shared_ptr <> 및 boost :: excim_ptr <>.

다른 팁

다중 관련이없는 생성 shared_ptr같은 개체에 :

#include <stdio.h>
#include "boost/shared_ptr.hpp"

class foo
{
public:
    foo() { printf( "foo()\n"); }

    ~foo() { printf( "~foo()\n"); }
};

typedef boost::shared_ptr<foo> pFoo_t;

void doSomething( pFoo_t p)
{
    printf( "doing something...\n");
}

void doSomethingElse( pFoo_t p)
{
    printf( "doing something else...\n");
}

int main() {
    foo* pFoo = new foo;

    doSomething( pFoo_t( pFoo));
    doSomethingElse( pFoo_t( pFoo));

    return 0;
}

함수 호출에 대한 인수 내부에 익명의 임시 공유 포인터 구성을 구성합니다.

f(shared_ptr<Foo>(new Foo()), g());

이것은 허용되기 때문입니다 new Foo() 그러므로 실행됩니다 g() 호출 및 g() 예외없이 shared_ptr 설정된 적이 있습니다 shared_ptr 청소할 기회가 없습니다 Foo 물체.

같은 물체에 두 개의 포인터를 조심하십시오.

boost::shared_ptr<Base> b( new Derived() );
{
  boost::shared_ptr<Derived> d( b.get() );
} // d goes out of scope here, deletes pointer

b->doSomething(); // crashes

대신 이것을 사용하십시오

boost::shared_ptr<Base> b( new Derived() );
{
  boost::shared_ptr<Derived> d = 
    boost::dynamic_pointer_cast<Derived,Base>( b );
} // d goes out of scope here, refcount--

b->doSomething(); // no crash

또한 shared_ptrs를 보유한 모든 클래스는 사본 생성자 및 할당 연산자를 정의해야합니다.

생성자에서 shared_from_this ()를 사용하려고하지 마십시오. 작동하지 않습니다. 대신 정적 메소드를 만들어 클래스를 생성하고 shared_ptr을 반환하도록하십시오.

나는 문제없이 shared_ptrs에 대한 언급을 전달했다. 저장되기 전에 복사했는지 확인하십시오 (즉, 클래스 멤버로서의 참조가 없음).

피해야 할 두 가지가 있습니다.

  • 전화 get() 기능이 원시 포인터를 가져 와서 포인트-투 객체가 범위를 벗어난 후에 사용합니다.

  • 참조 또는 원시 포인터를 shared_ptr 내부 카운트를 증가시키지 않아서 물체를 살리게하는 데 도움이되므로 위험해야합니다.

우리는 몇 주 이상 이상한 행동을 디버그했습니다.

그 이유는 다음과 같습니다.
우리는 'shared_from_this'대신 일부 스레드 작업자에게 'this'를 전달했습니다.

정확히 풋군은 아니지만, C ++ 0x 방식으로 머리를 감싸는 데 머리를 감싸기 전까지는 좌절의 원천입니다. <functional> 잘 놀지 마세요 shared_ptr. 행복하게, std::tr1::mem_fn 객체, 포인터 및로 작동합니다 shared_ptrs, 교체 std::mem_fun, 그러나 당신이 사용하고 싶다면 std::negate, std::not1, std::plus 또는 그 오랜 친구 중 하나 shared_ptr, 아늑해질 준비를하십시오 std::tr1::bind 그리고 아마도 논쟁 자리 소유자들도 마찬가지입니다. 실제로 이것은 실제로 훨씬 더 일반적인 것입니다. 이제 기본적으로 사용하기 때문입니다. bind 모든 기능 객체 어댑터에 대해서는 STL의 편의 기능에 이미 익숙하다면 익숙해지는 데 약간 익숙해집니다.

이 DDJ 기사 많은 예제 코드와 함께 주제에 대한 접촉. 나도 블로그 몇 년 전 처음에 어떻게 해야하는지 알아 내야했을 때.

사용 shared_ptr 정말 작은 물체의 경우 (좋아요 char short) 힙에 작은 물체가 많지만 실제로는 "공유"되지 않으면 오버 헤드가 될 수 있습니다. boost::shared_ptr G ++ 4.4.3에서 생성하는 모든 새로운 참조 수에 대해 16 바이트를 할당합니다. std::tr1::shared_ptr 20 바이트를 할당합니다. 이제 백만 명이 뚜렷한 경우 shared_ptr<char> 즉, 메모리의 2 천만 바이트가 카운트 = 1을 유지하는 데 사라 졌음을 의미합니다. 간접 비용과 기억 조각화는 말할 것도 없습니다. 좋아하는 플랫폼에서 다음을 사용해보십시오.

void * operator new (size_t size) {
  std::cout << "size = " << size << std::endl;
  void *ptr = malloc(size);
  if(!ptr) throw std::bad_alloc();
  return ptr;
}
void operator delete (void *p) {
  free(p);
}

클래스 정의 내부에서 shared_ptr <t>를 제공하는 것도 위험합니다. 대신 enabled_shared_from_this를 사용하십시오.

다음 게시물을 참조하십시오 여기

사용할 때주의해야합니다 shared_ptr 멀티 스테일 코드에서. 그러면 부부가 shared_ptr동일한 메모리를 가리키는 S는 다른 스레드에 의해 사용됩니다.

shared_ptr의 대중적인 광범위한 사용은 필연적으로 원치 않는, 보이지 않는 메모리 점령을 유발할 것입니다.

주기적 참조는 잘 알려진 원인이며 일부는 간접적이고 특히 하나 이상의 프로그래머가 수행하는 복잡한 코드에서 발견하기가 어려울 수 있습니다. 프로그래머는 한 객체가 빠른 수정으로 다른 객체에 대한 참조가 필요하다고 결정할 수 있으며 모든 코드를 검사하여 사이클을 닫고 있는지 확인할 시간이 없습니다. 이 위험은 크게 과소 평가되었습니다.

덜 잘 이해되지 않은 참고 문헌의 문제는 잘 이해되지 않습니다. 객체가 많은 shared_ptrs와 공유되면 각각의 객체가 제로화되거나 범위를 벗어날 때까지 파괴되지 않습니다. 이러한 참조 중 하나를 간과하고 끝났다고 생각한 기억에 숨어있는 물체로 끝나는 것은 매우 쉽습니다.

엄격하게 말하지만 이것들은 메모리 누출이 아니지만 (프로그램이 종료되기 전에 모두 풀릴 것입니다) 그들은 해롭고 탐지하기가 더 어렵습니다.

이러한 문제는 편리한 허위 선언의 결과입니다. 1. shared_ptr로 단일 소유권이되고 싶은 것을 선언합니다. scoped_ptr는 정확하지만 해당 객체에 대한 다른 언급은 원시 포인터 여야하며, 이는 매달려있을 수 있습니다. 2. Shared_ptr과 같은 수동적 관찰 참조가되고 싶은 것을 정말로 선언합니다. 약한 _ptr은 정확하지만 사용할 때마다 share_ptr로 변환하는 번거 로움이 있습니다.

나는 당신의 프로젝트 가이 관행이 당신을 얻을 수있는 문제의 종류의 훌륭한 예라고 생각합니다.

메모리 집중 애플리케이션이있는 경우 디자인이 객체 수명을 명시 적으로 제어 할 수 있도록 단일 소유권이 필요합니다.

단일 소유권 OpObject = null; 객체를 확실히 삭제하면 이제 그렇게 할 것입니다.

공유 소유권으로 spobject = null; ........누가 알아?......

공유 객체 (예 : 모든 활성 인스턴스 목록)의 레지스트리가있는 경우 객체는 자유롭지 않습니다. 솔루션 : 원형 의존성 구조의 경우 (Kaz Dragon의 답변 참조), 약점을 적절하게 사용하십시오.

스마트 포인터는 모든 것을위한 것은 아니며, 원시 포인터를 제거 할 수는 없습니다.

아마도 최악의 위험은 그 이후로입니다 shared_ptr 유용한 도구입니다. 사람들은 매번 그것을 넣을 것입니다. 평범한 포인터는 잘못 사용될 수 있으므로 같은 사람들이 생 포인터를 사냥하고 말이 안되는 경우에도 문자열, 용기 또는 스마트 포인터로 교체하려고 시도합니다. 원시 포인터의 합법적 인 사용은 용의자가 될 것입니다. 포인터 경찰이있을 것입니다.

이것은 아마도 최악의 위험 일뿐 만 아니라 유일한 심각한 위험 일 수 있습니다. 모든 최악의 학대 shared_ptr 스마트 포인터가 원시 포인터보다 우수하다는 아이디어의 직접적인 결과가 될 것이며 (그 의미가 무엇이든) 스마트 포인터를 어디에나 넣으면 C ++ 프로그래밍을 "더 안전하게"할 수 있습니다.

물론 스마트 포인터가 원시 포인터로 변환되어 사용하기 위해 원시 포인터로 변환해야한다는 사실은 스마트 포인터 컬트의 이러한 주장을 반박하지만, 원시 포인터 액세스가 "암시 적"이라는 사실을 반박합니다. operator*, operator-> (또는 명시 적 get()), 암시 적 변환에 암시되지는 않지만, 이것이 실제로 변환이 아니며,이 비 전환에 의해 생성 된 원시 포인터는 무해한 임시라는 인상을주기에 충분합니다.

C ++는 "안전한 언어"로 만들 수 없으며 C ++의 유용한 하위 집합은 "안전"입니다.

물론 C ++의 안전한 하위 집합이 작기 때문에 C ++의 LISP, Haskell, Java 등의 "메모리 안전"의 엄격한 의미에서 안전한 하위 집합 ( "안전한"은 "메모리 안전"의 엄격한 의미에서 "안전한"을 추구합니다. 안전하지 않은 프리미티브가 예외보다는 규칙이기 때문에 거의 쓸모가 없습니다. C ++의 엄격한 메모리 안전은 포인터가 없으며 자동 스토리지 클래스를 포함한 참조 만 참조합니다. 그러나 언어로 프로그래머는 정의에 의해 신뢰됩니다, 어떤 사람들은 원칙적으로 바보 "스마트 포인터"를 사용한다고 주장 할 것입니다. 하나의 구체적인 방법 프로그램 상태를 망치는 것은 피합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top