수색…


통사론

  • std::shared_ptr<ClassType> variableName = std::make_shared<ClassType>(arg1, arg2, ...);
  • std::shared_ptr<ClassType> variableName (new ClassType(arg1, arg2, ...));
  • std::unique_ptr<ClassType> variableName = std::make_unique<ClassType>(arg1, arg2, ...); // C ++ 14
  • std::unique_ptr<ClassType> variableName (new ClassType(arg1, arg2, ...));

비고

C ++은 메모리 관리 언어가 아닙니다. 동적으로 (로 만든 즉, 객체 메모리를 할당 new 명시 적으로 (로 할당이 해제되지 않은 경우 "유출"됩니다) delete ). 동적으로 할당 된 메모리가 그 객체에 대한 마지막 포인터를 버리기 전에 해제되는 것을 보장하는 것은 프로그래머의 책임입니다.

스마트 포인터를 사용하여 동적으로 할당 된 메모리의 범위를 자동으로 관리 할 수 ​​있습니다 (예 : 마지막 포인터 참조가 범위를 벗어나 삭제 된 경우).

스마트 포인터는 대부분 "원시"포인터보다 선호됩니다. 동적으로 할당 된 메모리의 소유권 의미를 명시 적으로 나타내며, 객체가 공유되거나 고유하게 소유되도록 의도되었는지 여부를 그들의 이름으로 전달합니다.

스마트 포인터를 사용하려면 #include <memory> 를 사용하십시오.

소유권 공유 (std :: shared_ptr)

클래스 템플릿 std::shared_ptr 은 객체의 소유권을 다른 공유 포인터와 공유 할 수있는 공유 포인터를 정의합니다. 이는 독점권을 나타내는 std::unique_ptr 과 대조됩니다.

공유 동작은 개체를 가리키는 공유 포인터의 수와 함께 참조 계산으로 알려진 기술을 통해 구현됩니다. 마지막 std::shared_ptr 인스턴스의 삭제 또는 재 할당을 통해이 수가 0에 도달하면 객체가 자동으로 삭제됩니다.


// Creation: 'firstShared' is a shared pointer for a new instance of 'Foo'
std::shared_ptr<Foo> firstShared = std::make_shared<Foo>(/*args*/);

동일한 객체를 공유하는 여러 스마트 포인터를 만들려면 첫 번째 공유 포인터의 별칭 인 다른 shared_ptr 을 만들어야합니다. 다음과 같은 두 가지 방법이 있습니다.

std::shared_ptr<Foo> secondShared(firstShared);  // 1st way: Copy constructing
std::shared_ptr<Foo> secondShared;
secondShared = firstShared;                      // 2nd way: Assigning

위의 방법 중 하나는 secondSharedfirstSharedFoo 인스턴스의 소유권을 공유하는 공유 포인터로 firstShared .

스마트 포인터는 원시 포인터처럼 작동합니다. 즉, * 를 사용하여 참조를 취소 할 수 있습니다. 일반 연산자 -> 연산자도 사용할 수 있습니다.

secondShared->test(); // Calls Foo::test()

마지막으로 별명이 지정된 shared_ptr 이 범위를 벗어나면 Foo 인스턴스의 소멸자가 호출됩니다.

경고 : 공유 소유권 의미에 대한 추가 데이터를 할당해야하는 경우 shared_ptr 하면 bad_alloc 예외가 발생할 수 있습니다. 생성자에 일반 포인터가 전달되면 지정된 객체를 소유하고 있다고 가정하고 예외가 throw되면 deleter를 호출합니다. 이것은 shared_ptr<T>(new T(args))shared_ptr<T> 할당이 실패 할 경우 T 객체를 누설하지 않는다는 것을 의미합니다. 그러나 make_shared<T>(args) 또는 allocate_shared<T>(alloc, args) 를 사용하여 메모리 할당을 최적화하는 것이 좋습니다.


shared_ptr을 사용하여 배열 ([]) 할당

C ++ 11 C ++ 17

불행하게도, make_shared<> 사용하여 배열을 할당하는 직접적인 방법은 없습니다.

newstd::default_delete 사용하여 shared_ptr<> 대한 배열을 만들 수 있습니다.

예를 들어 10 개의 정수 배열을 할당하려면 다음과 같이 코드를 작성할 수 있습니다.

shared_ptr<int> sh(new int[10], std::default_delete<int[]>());

std::default_delete 지정하면 할당 된 메모리가 delete[] 사용하여 올바르게 정리되었는지 확인해야합니다.

컴파일 시간에 크기를 알면 다음과 같이 할 수 있습니다.

template<class Arr>
struct shared_array_maker {};
template<class T, std::size_t N>
struct shared_array_maker<T[N]> {
  std::shared_ptr<T> operator()const{
    auto r = std::make_shared<std::array<T,N>>();
    if (!r) return {};
    return {r.data(), r};
  }
};
template<class Arr>
auto make_shared_array()
-> decltype( shared_array_maker<Arr>{}() )
{ return shared_array_maker<Arr>{}(); }

make_shared_array<int[10]> 는 10 개의 정수를 가리키는 shared_ptr<int> 반환한다.

C ++ 17

C ++ 17에서 shared_ptr 배열 유형에 대한 특별한 지원얻었습니다 . 더 이상 명시 적으로 배열 삭제자를 지정할 필요가 없으며 [] 배열 인덱스 연산자를 사용하여 공유 포인터를 역 참조 할 수 있습니다.

std::shared_ptr<int[]> sh(new int[10]);
sh[0] = 42;

공유 포인터는 자신이 소유 한 객체의 하위 객체를 가리킬 수 있습니다.

struct Foo { int x; };
std::shared_ptr<Foo> p1 = std::make_shared<Foo>();
std::shared_ptr<int> p2(p1, &p1->x);

p2p1 모두 Foo 유형의 객체를 소유하지만 p2int 멤버 x 가리 킵니다. 즉, p1 이 범위를 벗어나거나 재 할당되는 경우 기본 Foo 객체가 여전히 작동하여 p2 가 매달리지 않도록합니다.


중요 : shared_ptr 은 별칭 생성자로 만든 자체 및 모든 다른 shared_ptr 에 대해서만 알고 있습니다. 동일한 Foo 인스턴스에 대한 참조로 생성 된 다른 모든 shared_ptr 포함하여 다른 포인터에 대해서는 알지 못합니다.

Foo *foo = new Foo;
std::shared_ptr<Foo> shared1(foo);
std::shared_ptr<Foo> shared2(foo); // don't do this

shared1.reset(); // this will delete foo, since shared1
                 // was the only shared_ptr that owned it

shared2->test(); // UNDEFINED BEHAVIOR: shared2's foo has been
                 // deleted already!!

shared_ptr의 소유권 이전

기본적으로 shared_ptr 은 참조 횟수를 증가시키고 소유권을 이전하지 않습니다. 그러나 std::move 사용하여 소유권을 이전 할 수 있습니다.

shared_ptr<int> up = make_shared<int>();
// Transferring the ownership
shared_ptr<int> up2 = move(up);
// At this point, the reference count of up = 0 and the
// ownership of the pointer is solely with up2 with reference count = 1

임시 소유권으로 공유 (std :: weak_ptr)

std::weak_ptr 인스턴스는 std::shared_ptr 인스턴스가 소유 한 객체를 가리킬 수 있지만 일시적인 소유자 만이 될 수 있습니다. 즉, 약한 포인터는 개체의 참조 횟수를 변경하지 않으므로 모든 개체의 공유 포인터가 다시 할당되거나 파괴되면 개체의 삭제를 방지하지 못합니다.


다음 예제에서 std::weak_ptr 인스턴스는 트리 객체의 삭제가 금지되지 않도록 사용됩니다.

#include <memory>
#include <vector>

struct TreeNode {
    std::weak_ptr<TreeNode> parent;
    std::vector< std::shared_ptr<TreeNode> > children;
};

int main() {
    // Create a TreeNode to serve as the root/parent.
    std::shared_ptr<TreeNode> root(new TreeNode);

    // Give the parent 100 child nodes.
    for (size_t i = 0; i < 100; ++i) {
        std::shared_ptr<TreeNode> child(new TreeNode);
        root->children.push_back(child);
        child->parent = root;
    }

    // Reset the root shared pointer, destroying the root object, and
    // subsequently its child nodes.
    root.reset();
}

자식 노드가 루트 노드의 자식 노드에 추가 될 때 std::weak_ptr 멤버 parent 는 루트 노드로 설정됩니다. 멤버 parent 는 루트 포인터의 참조 횟수가 증가하지 않도록 공유 포인터와 달리 약 포인터로 선언됩니다. 루트 노드가 main() 의 끝에서 재설정되면 루트가 삭제됩니다. 유일하게 남아 있기 때문에 std::shared_ptr 자식 노드에 대한 참조가 루트의 수집에 포함 된 children , 모든 자식 노드 이후도 파괴된다.

제어 블록 구현 세부 사항으로 인해 shared_ptr 참조 카운터와 weak_ptr 참조 카운터가 모두 0에 도달 할 때까지 shared_ptr 할당 메모리가 해제되지 않을 수 있습니다.

#include <memory>
int main()
{
    {
         std::weak_ptr<int> wk;
         {
             // std::make_shared is optimized by allocating only once 
             // while std::shared_ptr<int>(new int(42)) allocates twice.
             // Drawback of std::make_shared is that control block is tied to our integer
             std::shared_ptr<int> sh = std::make_shared<int>(42);
             wk = sh;
             // sh memory should be released at this point...
         }
         // ... but wk is still alive and needs access to control block
     }
     // now memory is released (sh and wk)
}

이후 std::weak_ptr 살아 통해 직접 데이터 액세스 참조 된 개체를 유지하지 않습니다 std::weak_ptr 불가능합니다. 대신 참조 된 객체에 대해 std::shared_ptr 을 검색하려고 시도하는 lock() 멤버 함수를 제공합니다.

#include <cassert>
#include <memory>
int main()
{
    {
         std::weak_ptr<int> wk;
         std::shared_ptr<int> sp;
         {
             std::shared_ptr<int> sh = std::make_shared<int>(42);
             wk = sh;
             // calling lock will create a shared_ptr to the object referenced by wk
             sp = wk.lock();
             // sh will be destroyed after this point, but sp is still alive
         }
         // sp still keeps the data alive.
         // At this point we could even call lock() again 
         // to retrieve another shared_ptr to the same data from wk
         assert(*sp == 42);
         assert(!wk.expired());
         // resetting sp will delete the data,
         // as it is currently the last shared_ptr with ownership
         sp.reset();
         // attempting to lock wk now will return an empty shared_ptr,
         // as the data has already been deleted
         sp = wk.lock();
         assert(!sp);
         assert(wk.expired());
     }
}

고유 소유권 (std :: unique_ptr)

C ++ 11

std::unique_ptr 은 동적으로 저장된 객체의 수명을 관리하는 클래스 템플릿입니다. std::shared_ptr 과는 달리, 동적 객체는 언제든지 std::unique_ptr한 인스턴스 에 의해서만 소유되며,


// Creates a dynamic int with value of 20 owned by a unique pointer
std::unique_ptr<int> ptr = std::make_unique<int>(20);

(참고 : std::unique_ptr 은 C ++ 11부터 사용할 수 있으며 std::make_unique 는 C ++ 14부터 사용할 수 있습니다.)

변수 ptr 만 동적으로 할당 된 int 대한 포인터를 보유합니다. 객체를 소유하고있는 유일한 포인터가 범위를 벗어나면 소유 된 객체가 삭제됩니다. 즉, 객체가 클래스 유형이고 해당 객체의 메모리가 해제되면 소멸자가 호출됩니다.

배열 유형과 함께 std::unique_ptrstd::make_unique 를 사용하려면 배열 전문화를 사용하십시오.

// Creates a unique_ptr to an int with value 59
std::unique_ptr<int> ptr = std::make_unique<int>(59);

// Creates a unique_ptr to an array of 15 ints
std::unique_ptr<int[]> ptr = std::make_unique<int[]>(15);

당신은 원시 포인터처럼 std::unique_ptr 접근 할 수있다.


원래의 스마트 포인터가 nullptr 가리키는 std::move 를 사용하여 스마트 포인터의 내용 소유권을 다른 포인터로 전송할 수 있습니다.

// 1. std::unique_ptr
std::unique_ptr<int> ptr = std::make_unique<int>();

// Change value to 1
*ptr = 1;

// 2. std::unique_ptr (by moving 'ptr' to 'ptr2', 'ptr' doesn't own the object anymore)
std::unique_ptr<int> ptr2 = std::move(ptr);

int a = *ptr2; // 'a' is 1
int b = *ptr;  // undefined behavior! 'ptr' is 'nullptr'
               // (because of the move command above)

unique_ptr 을 매개 변수로 함수에 전달 :

void foo(std::unique_ptr<int> ptr)
{
    // Your code goes here
}

std::unique_ptr<int> ptr = std::make_unique<int>(59);
foo(std::move(ptr))

함수에서 unique_ptr 을 반환합니다. 이는 반환의 소유권 의미를 명확하게 전달하기 때문에 공장 함수를 작성하는 데있어 C ++에서 선호하는 11 가지 방법입니다. 호출자가 결과 unique_ptr 소유하고 이에 대한 책임이 있습니다.

std::unique_ptr<int> foo()
{
    std::unique_ptr<int> ptr = std::make_unique<int>(59);
    return ptr;
}

std::unique_ptr<int> ptr = foo();

이것을 다음과 비교하십시오 :

int* foo_cpp03();

int* p = foo_cpp03(); // do I own p? do I have to delete it at some point?
                      // it's not readily apparent what the answer is.
C ++ 14

클래스 템플릿 make_unique 는 C ++ 14부터 제공됩니다. 수동으로 C ++ 11 코드에 손쉽게 추가 할 수 있습니다.

template<typename T, typename... Args>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(Args&&... args)
{ return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); }

// Use make_unique for arrays
template<typename T>
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(size_t n)
{ return std::unique_ptr<T>(new typename std::remove_extent<T>::type[n]()); }
C ++ 11

벙어리 스마트 포인터 ( std::auto_ptr )와 달리, unique_ptr 은 벡터 할당 ( std::vector 아닌) 으로 인스턴스화 될 수도 있습니다. 이전 예제는 스칼라 할당에 대한 것이 었습니다. 예를 들어, 10 개의 요소에 대해 동적으로 할당 된 정수 배열을 가지려면 int[] 를 ( int 아닌) 템플릿 유형으로 지정합니다.

std::unique_ptr<int[]> arr_ptr = std::make_unique<int[]>(10);

다음과 같이 단순화 할 수 있습니다.

auto arr_ptr = std::make_unique<int[]>(10);

자, arr_ptr 을 배열처럼 사용 arr_ptr :

arr_ptr[2] =  10; // Modify third element

할당 취소에 대해 걱정할 필요가 없습니다. 이 템플릿 특수 버전은 생성자와 소멸자를 적절하게 호출합니다. 벡터화 된 버전의 unique_ptr 또는 vector 자체를 사용하는 것은 개인적인 선택입니다.

C ++ 11 이전 버전에서는 std::auto_ptr 을 사용할 수있었습니다. unique_ptr 과 달리 auto_ptr 을 복사 할 수 있습니다.이 때 소스 ptr 은 포함 된 포인터의 소유권을 잃고 대상은 그것을받습니다.

사용자 정의 삭제자를 사용하여 C 인터페이스에 대한 래퍼 생성

SDL2 와 같은 많은 C 인터페이스에는 자체 삭제 기능이 있습니다. 즉, 스마트 포인터를 직접 사용할 수 없습니다.

std::unique_ptr<SDL_Surface> a; // won't work, UNSAFE!

대신 자신 만의 Deleter를 정의해야합니다. 여기의 예제는 SDL_FreeSurface() 함수를 사용하여 해제해야하는 SDL_Surface 구조체를 사용하지만 다른 많은 C 인터페이스에도 적용 할 수 있어야합니다.

deleter는 포인터 인수로 호출 할 수 있어야하므로 예를 들어 간단한 함수 포인터 일 수 있습니다.

std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> a(pointer, SDL_FreeSurface);

다른 호출 가능한 객체도 작동합니다 (예 : operator() 있는 클래스 operator() .

struct SurfaceDeleter {
    void operator()(SDL_Surface* surf) {
        SDL_FreeSurface(surf);
    }
};

std::unique_ptr<SDL_Surface, SurfaceDeleter> a(pointer, SurfaceDeleter{}); // safe
std::unique_ptr<SDL_Surface, SurfaceDeleter> b(pointer); // equivalent to the above
                                                         // as the deleter is value-initialized

이것은 안전하고 zero overhead ( unique_ptr 을 사용하는 경우) 자동 메모리 관리를 제공 할뿐만 아니라 예외 안전성도 얻습니다.

Deleter는 unique_ptr 에 대한 유형의 일부이며, 구현시 빈 기본 최적화 를 사용하여 빈 사용자 정의 삭제 자의 크기가 변경되지 않도록 할 수 있습니다. 따라서 std::unique_ptr<SDL_Surface, SurfaceDeleter>std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> 는 같은 문제를 비슷한 방식으로 해결하지만 이전 유형은 여전히 ​​포인터의 크기뿐입니다. 후자의 타입은 포인터 ( SDL_Surface* 와 함수 포인터 모두)를 가지고 있어야합니다! 자유 함수 사용자 정의 deleter를 사용하는 경우 빈 형식으로 함수를 래핑하는 것이 좋습니다.

참조 카운팅이 중요한 경우 unique_ptr 대신 shared_ptr 사용할 수 있습니다. shared_ptr 항상 deleter를 저장하며 API에서 유용 할 수있는 deleter의 유형을 지 웁니다. unique_ptr 비해 shared_ptr 을 사용하는 단점은 참조 자 수를 유지하기위한 deleter 및 성능 비용을 저장하는 데 더 높은 메모리 비용이 포함된다는 것입니다.

// deleter required at construction time and is part of the type
std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> a(pointer, SDL_FreeSurface);

// deleter is only required at construction time, not part of the type
std::shared_ptr<SDL_Surface> b(pointer, SDL_FreeSurface); 
C ++ 17

template auto 으로 우리는 커스텀 deleter를 더 쉽게 래핑 할 수 있습니다 :

template <auto DeleteFn>
struct FunctionDeleter {
    template <class T>
    void operator()(T* ptr) {
        DeleteFn(ptr);
    }
};

template <class T, auto DeleteFn>
using unique_ptr_deleter = std::unique_ptr<T, FunctionDeleter<DeleteFn>>;

위의 예는 다음과 같습니다.

unique_ptr_deleter<SDL_Surface, SDL_FreeSurface> c(pointer);

여기서 auto 의 목적은 void (예 : SDL_FreeSurface )이든 SDL_FreeSurface (예 : fclose ) 모든 자유 함수를 처리하는 것입니다.

이동 의미없는 고유 한 소유권 (auto_ptr)

C ++ 11

참고 : std::auto_ptr 은 C ++ 11에서 사용되지 않으며 C ++ 17에서 제거됩니다. C ++ 03 이전 버전을 사용하도록 강요 받고 조심스럽게 사용하려는 경우에만 사용해야합니다. std::auto_ptr 동작을 대체하려면 std::move 와 함께 unique_ptr로 이동하는 것이 좋습니다.

우리가 std::unique_ptr 을 갖기 전에, 우리는 이동의 의미를 std::unique_ptr 전에 std::auto_ptr 가졌다. std::auto_ptr 은 고유 한 소유권을 제공하지만 복사시 소유권을 이전합니다.

모든 스마트 포인터와 마찬가지로 std::auto_ptr 자동으로 자원을 정리합니다 ( RAII 참조).

{
    std::auto_ptr<int> p(new int(42));
    std::cout << *p;
} // p is deleted here, no memory leaked

한 명의 소유자 만 허용합니다.

std::auto_ptr<X> px = ...;
std::auto_ptr<X> py = px; 
  // px is now empty 

이렇게하면 std :: auto_ptr을 사용하여 의도하지 않은 소유권을 잃을 위험이있는 소유권을 명확하고 고유하게 유지할 수 있습니다.

void f(std::auto_ptr<X> ) {
    // assumes ownership of X
    // deletes it at end of scope
};

std::auto_ptr<X> px = ...;
f(px); // f acquires ownership of underlying X
       // px is now empty
px->foo(); // NPE!
// px.~auto_ptr() does NOT delete

소유권 이전은 "복사"생성자에서 발생했습니다. auto_ptr 의 복사 생성자와 복사 할당 연산자는 피연산자를 수정 가능하도록 비 const 참조로 사용합니다. 구현 예는 다음과 같습니다.

template <typename T>
class auto_ptr {
    T* ptr;
public:
    auto_ptr(auto_ptr& rhs)
    : ptr(rhs.release())
    { }

    auto_ptr& operator=(auto_ptr& rhs) {
        reset(rhs.release());
        return *this;
    }

    T* release() {
        T* tmp = ptr;
        ptr = nullptr;
        return tmp;
    }

    void reset(T* tmp = nullptr) {
        if (ptr != tmp) {
            delete ptr;
            ptr = tmp;
        }
    }

    /* other functions ... */
};

이것은 복사 의미론을 깨뜨리는데, 이는 객체 복사가 두 개의 동등한 버전으로 이루어져야한다는 것을 의미합니다. 복사 가능한 유형 인 T 과 같이 작성할 수 있습니다.

T a = ...;
T b(a);
assert(b == a);

그러나 auto_ptr 의 경우에는 그렇지 않습니다. 따라서 auto_ptr 을 컨테이너에 두는 것은 안전하지 않습니다.

이것을 가리키는 shared_ptr 얻기

enable_shared_from_thisthis 위해 유효한 shared_ptr 인스턴스를 얻을 수있게합니다.

클래스 템플릿에서 클래스를 파생하여 enable_shared_from_this , 당신은 방법 상속 shared_from_this 반환 shared_ptr 에 인스턴스를 this .

객체가로 작성되어야합니다 shared_ptr 첫 번째 장소에서 :

#include <memory>
class A: public enable_shared_from_this<A> {
};
A* ap1 =new A();
shared_ptr<A> ap2(ap1); // First prepare a shared pointer to the object and hold it!
// Then get a shared pointer to the object from the object itself
shared_ptr<A> ap3 = ap1->shared_from_this(); 
int c3 =ap3.use_count(); // =2: pointing to the same object

(2) 생성자 내에서 enable_shared_from_this 호출 할 수 없습니다.

#include <memory> // enable_shared_from_this

class Widget : public std::enable_shared_from_this< Widget >
{
public:
    void DoSomething()
    {
        std::shared_ptr< Widget > self = shared_from_this();
        someEvent -> Register( self );
    }
private:
    ...
};

int main()
{
    ...
    auto w = std::make_shared< Widget >();
    w -> DoSomething();
    ...
}

shared_ptr 소유하지 않은 객체 shared_from_this() 예 : 로컬 자동 객체 또는 전역 객체 shared_from_this() 에서 shared_from_this() 를 사용하면 동작이 정의되지 않습니다. C ++ 17 이후로 std::bad_alloc 던졌습니다.

생성자에서 shared_from_this() 를 사용하는 것은 shared_ptr 소유하지 않은 객체 shared_ptr 객체를 사용하는 것과 같습니다. 왜냐하면 생성자가 반환 한 후에 shared_ptr 이 객체를 소유하기 때문입니다.

주조 표준 :: shared_ptr 포인터

std::shared_ptr 에서 static_cast , const_cast , dynamic_castreinterpret_cast 를 직접 사용하여 인수로 전달되는 포인터로 소유권을 공유하는 포인터를 검색 할 수 없습니다. 대신 함수 std::static_pointer_cast , std::const_pointer_cast , std::dynamic_pointer_caststd::reinterpret_pointer_cast 사용해야합니다.

struct Base { virtual ~Base() noexcept {}; };
struct Derived: Base {};
auto derivedPtr(std::make_shared<Derived>());
auto basePtr(std::static_pointer_cast<Base>(derivedPtr));
auto constBasePtr(std::const_pointer_cast<Base const>(basePtr));
auto constDerivedPtr(std::dynamic_pointer_cast<Derived const>(constBasePtr));

std::reinterpret_pointer_castN3920에서 제안하고 2014 년 2 월 에 Library Fundamentals TS 채택 되었기 때문에 C ++ 11 및 C ++ 14에서는 사용할 수 없습니다. 그러나 다음과 같이 구현할 수 있습니다.

template <typename To, typename From>
inline std::shared_ptr<To> reinterpret_pointer_cast(
    std::shared_ptr<From> const & ptr) noexcept
{ return std::shared_ptr<To>(ptr, reinterpret_cast<To *>(ptr.get())); }

스마트 포인터 작성 : value_ptr

value_ptr 은 값처럼 동작하는 스마트 포인터입니다. 복사되면 내용을 복사합니다. 생성되면 내용을 생성합니다.

// Like std::default_delete:
template<class T>
struct default_copier {
  // a copier must handle a null T const* in and return null:
  T* operator()(T const* tin)const {
    if (!tin) return nullptr;
    return new T(*tin);
  }
  void operator()(void* dest, T const* tin)const {
    if (!tin) return;
    return new(dest) T(*tin);
  }
};
// tag class to handle empty case:
struct empty_ptr_t {};
constexpr empty_ptr_t empty_ptr{};
// the value pointer type itself:
template<class T, class Copier=default_copier<T>, class Deleter=std::default_delete<T>,
  class Base=std::unique_ptr<T, Deleter>
>
struct value_ptr:Base, private Copier {
  using copier_type=Copier;
  // also typedefs from unique_ptr

  using Base::Base;

  value_ptr( T const& t ):
    Base( std::make_unique<T>(t) ),
    Copier()
  {}
  value_ptr( T && t ):
    Base( std::make_unique<T>(std::move(t)) ),
    Copier()
  {}
  // almost-never-empty:
      value_ptr():
    Base( std::make_unique<T>() ),
    Copier()
  {}
  value_ptr( empty_ptr_t ) {}

  value_ptr( Base b, Copier c={} ):
    Base(std::move(b)),
    Copier(std::move(c))
  {}

  Copier const& get_copier() const {
    return *this;
  }

  value_ptr clone() const {
    return {
      Base(
        get_copier()(this->get()),
        this->get_deleter()
      ),
      get_copier()
    };
  }
  value_ptr(value_ptr&&)=default;
  value_ptr& operator=(value_ptr&&)=default;

  value_ptr(value_ptr const& o):value_ptr(o.clone()) {}
  value_ptr& operator=(value_ptr const&o) {
    if (o && *this) {
      // if we are both non-null, assign contents:
      **this = *o;
    } else {
      // otherwise, assign a clone (which could itself be null):
      *this = o.clone();
    }
    return *this;
  }
  value_ptr& operator=( T const& t ) {
    if (*this) {
      **this = t;
    } else {
      *this = value_ptr(t);
    }
    return *this;
  }
  value_ptr& operator=( T && t ) {
    if (*this) {
      **this = std::move(t);
    } else {
      *this = value_ptr(std::move(t));
    }
    return *this;
  }
  T& get() { return **this; }
  T const& get() const { return **this; }
  T* get_pointer() {
    if (!*this) return nullptr;
    return std::addressof(get());
  }
  T const* get_pointer() const {
    if (!*this) return nullptr;
    return std::addressof(get());
  }
  // operator-> from unique_ptr
};
template<class T, class...Args>
value_ptr<T> make_value_ptr( Args&&... args ) {
  return {std::make_unique<T>(std::forward<Args>(args)...)};
}

이 특정 value_ptr은 empty_ptr_t 하거나 empty_ptr_t 이동할 경우에만 비어 있습니다. 그것은 그것이 unique_ptr 이라는 사실을 드러내 기 때문에 explicit operator bool() const 가 그것에 작용합니다. .get() 이 참조를 반환하도록 변경되었으므로 (거의 빈 상태가 .get_pointer() 가 대신 포인터를 반환합니다.

이 스마트 포인터는 값 의미를 원하지만 pImpl 의 내용을 구현 파일 외부에 노출하고 싶지 않은 pImpl 경우에 유용 할 수 있습니다.

기본이 아닌 Copier 사용하면 파생 된 인스턴스를 생성하고 값 유형으로 변환하는 방법을 알고있는 가상 기본 클래스도 처리 할 수 ​​있습니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow