질문은 제목에 정말 적합합니다.이 차이에 대한 기술적 이유가 무엇인지 알고 싶은데 그 이유도 궁금합니다.
std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;
답변
이 때문에 인 std::shared_ptr
반면, 구현 형 삭제 std::unique_ptr
하지 않습니다.
std::shared_ptr
유형 삭제를 구현 하기 때문에 또 다른 흥미로운 속성 인 viz 도 지원합니다 . 클래스 템플릿에 대한 템플릿 유형 인수로 삭제 자의 유형이 필요 하지 않습니다 . 그들의 선언을보십시오 :
template<class T,class Deleter = std::default_delete<T> >
class unique_ptr;
이는 보유 Deleter
하면서, 입력 파라미터로서
template<class T>
class shared_ptr;
없습니다.
이제 질문은 왜 shared_ptr
유형 삭제를 구현합니까? 음, 그렇게합니다. 참조 계산을 지원해야하고이를 지원하기 위해 힙에서 메모리를 할당해야 하고 어쨌든 메모리 를 할당해야하므로 한 단계 더 나아가 유형 삭제를 구현합니다. 이는 힙이 필요합니다. 할당도. 그래서 기본적으로 그것은 단지 기회 주의적입니다!
유형 삭제로 인해는 std::shared_ptr
두 가지를 지원할 수 있습니다.
- 이 같은 유형의 객체를 저장할 수 있습니다
void*
, 하지만 여전히 제대로 파괴에 개체를 삭제할 수 있습니다 올바르게에 의해 자신의 소멸자를 호출 . - deleter의 유형은 클래스 템플릿에 유형 인수로 전달되지 않으므로 type-safety를 손상시키지 않고 약간의 자유를 의미합니다 .
좋구나. 그것이 어떻게 std::shared_ptr
작동 하는지에 관한 것 입니다.
이제 질문은 std::unique_ptr
객체 를 저장할 수 void*
있습니까? 음, 대답은 그렇습니다 . 적절한 삭제자를 인수로 전달했다면 그렇습니다 . 다음은 그러한 데모 중 하나입니다.
int main()
{
auto deleter = [](void const * data ) {
int const * p = static_cast<int const*>(data);
std::cout << *p << " located at " << p << " is being deleted";
delete p;
};
std::unique_ptr<void, decltype(deleter)> p(new int(959), deleter);
} //p will be deleted here, both p ;-)
출력 ( 온라인 데모 ) :
959 located at 0x18aec20 is being deleted
댓글에 매우 흥미로운 질문을했습니다.
제 경우에는 유형 삭제 삭제자가 필요하지만 가능해 보입니다 (일부 힙 할당 비용으로). 기본적으로 이것은 실제로 세 번째 유형의 스마트 포인터에 대한 틈새 지점이 있음을 의미합니까 : 유형 삭제가있는 독점 소유권 스마트 포인터입니다.
이는에 @ 스티브 Jessop는 다음과 같은 솔루션을 제안,
나는 실제로 이것을 시도한 적이 없지만 아마도 삭제 자
std::function
유형으로 적절한 것을 사용하여 달성 할 수unique_ptr
있습니까? 실제로 작동한다고 가정하면 독점 소유권과 유형 삭제 삭제가 완료됩니다.
이 제안에 따라 이것을 구현했습니다 ( std::function
필요하지 않은 것처럼 사용 하지는 않지만).
using unique_void_ptr = std::unique_ptr<void, void(*)(void const*)>;
template<typename T>
auto unique_void(T * ptr) -> unique_void_ptr
{
return unique_void_ptr(ptr, [](void const * data) {
T const * p = static_cast<T const*>(data);
std::cout << "{" << *p << "} located at [" << p << "] is being deleted.\n";
delete p;
});
}
int main()
{
auto p1 = unique_void(new int(959));
auto p2 = unique_void(new double(595.5));
auto p3 = unique_void(new std::string("Hello World"));
}
출력 ( 온라인 데모 ) :
{Hello World} located at [0x2364c60] is being deleted.
{595.5} located at [0x2364c40] is being deleted.
{959} located at [0x2364c20] is being deleted.
도움이 되었기를 바랍니다.
답변
이론적 근거 중 하나는 a의 많은 사용 사례 중 하나에 있습니다. shared_ptr
즉, 수명 지표 또는 보초로 사용됩니다.
이것은 원래의 부스트 문서에서 언급되었습니다.
auto register_callback(std::function<void()> closure, std::shared_ptr<void> pv)
{
auto closure_target = { closure, std::weak_ptr<void>(pv) };
...
// store the target somewhere, and later....
}
void call_closure(closure_target target)
{
// test whether target of the closure still exists
auto lock = target.sentinel.lock();
if (lock) {
// if so, call the closure
target.closure();
}
}
closure_target
다음과 같은 것이 어디에 있습니까?
struct closure_target {
std::function<void()> closure;
std::weak_ptr<void> sentinel;
};
호출자는 다음과 같은 콜백을 등록합니다.
struct active_object : std::enable_shared_from_this<active_object>
{
void start() {
event_emitter_.register_callback([this] { this->on_callback(); },
shared_from_this());
}
void on_callback()
{
// this is only ever called if we still exist
}
};
shared_ptr<X>
는 항상로 변환 할 수 있기 때문에 shared_ptr<void>
event_emitter는 이제 다시 호출하는 객체의 유형을 인식하지 못할 수 있습니다.
이 배열은 구독자에게 교차 사례를 처리 할 의무 (active_object가 사라지는 동안 조치를 기다리는 동안 대기열에 콜백이 있다면 어떻게 될까요?)를 이벤트 이미 터에 해제하고 구독 취소를 동기화 할 필요가 없음을 의미합니다. weak_ptr<void>::lock
동기화 된 작업입니다.