"원시"포인터는 관리되지 않습니다. 즉, 다음 줄 :
SomeKindOfObject *someKindOfObject = new SomeKindOfObject();
... delete
적절한 시간에 동반자 가 실행되지 않으면 메모리가 누출 됩니다.
auto_ptr
이러한 경우를 최소화하기 위해 std::auto_ptr<>
도입되었습니다. 그러나 2011 년 표준 이전의 C ++의 한계로 인해 auto_ptr
메모리 누수는 여전히 쉽습니다 . 그러나 다음과 같은 제한된 경우에는 충분합니다.
void func() {
std::auto_ptr<SomeKindOfObject> sKOO_ptr(new SomeKindOfObject());
// do some work
// will not leak if you do not copy sKOO_ptr.
}
가장 약한 사용 사례 중 하나는 컨테이너입니다. 의 사본 auto_ptr<>
이 만들어지고 이전 사본이 신중하게 재설정되지 않으면 컨테이너가 포인터를 삭제하고 데이터를 잃을 수 있기 때문 입니다.
unique_ptr
C ++ 11은 std::unique_ptr<>
다음과 같은 대안을 도입했습니다 .
void func2() {
std::unique_ptr<SomeKindofObject> sKOO_unique(new SomeKindOfObject());
func3(sKOO_unique); // now func3() owns the pointer and sKOO_unique is no longer valid
}
이러한 a unique_ptr<>
는 기능간에 전달 될 때도 올바르게 정리됩니다. 포인터의 "소유권"을 의미 적으로 표현하여이를 수행합니다. "소유자"가이를 정리합니다. 따라서 컨테이너에 사용하기에 이상적입니다.
std::vector<std::unique_ptr<SomeKindofObject>> sKOO_vector();
달리가 auto_ptr<>
, unique_ptr<>
때 여기에 잘 행동하고, vector
크기 변경은 객체의 어느 것도 실수 동안 삭제되지 않고 vector
복사합니다 백업 저장소를.
shared_ptr
과 weak_ptr
unique_ptr<>
유용하지만 확실하지만 코드베이스의 두 부분이 동일한 객체를 참조하고 포인터를 복사하여 적절한 정리를 보장하려는 경우가 있습니다. 예를 들어, 다음을 사용할 때 트리는 다음과 같습니다 std::shared_ptr<>
.
template<class T>
struct Node {
T value;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
이 경우 루트 노드의 여러 복사본을 유지할 수 있으며 루트 노드의 모든 복사본이 삭제되면 트리가 올바르게 정리됩니다.
이것은 각각 shared_ptr<>
객체에 대한 포인터뿐만 아니라 shared_ptr<>
동일한 포인터를 참조하는 모든 객체 의 참조 카운트를 유지 하기 때문에 작동합니다 . 새로운 것이 만들어지면 카운트가 올라갑니다. 하나가 파괴되면 카운트가 내려갑니다. 카운트가 0에 도달하면 포인터는 delete
d입니다.
이중 링크 구조는 순환 참조로 끝납니다. parent
트리에 포인터 를 추가하고 싶다고 가정 해보십시오 Node
.
template<class T>
struct Node {
T value;
std::shared_ptr<Node<T>> parent;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
이제를 제거 Node
하면 순환 참조가 있습니다. delete
참조 카운트가 0이 아니기 때문에 절대 d가되지 않습니다.
이 문제를 해결하려면 다음을 사용하십시오 std::weak_ptr<>
.
template<class T>
struct Node {
T value;
std::weak_ptr<Node<T>> parent;
std::shared_ptr<Node<T>> left;
std::shared_ptr<Node<T>> right;
};
이제는 제대로 작동하며 노드를 제거해도 부모 노드에 대한 참조가 고정되지 않습니다. 그러나 나무를 걷는 것은 조금 더 복잡합니다.
std::shared_ptr<Node<T>> parent_of_this = node->parent.lock();
이런 식으로 노드에 대한 참조를 잠글 수 있으며 작업하는 동안 노드를 유지하고 있기 때문에 사라지지 않도록 합리적으로 보장 할 shared_ptr<>
수 있습니다.
make_shared
과 make_unique
지금, 거기에 약간의 문제가 있습니다 shared_ptr<>
와 unique_ptr<>
그 해결해야한다. 다음 두 줄에 문제가 있습니다.
foo_unique(std::unique_ptr<SomeKindofObject>(new SomeKindOfObject()), thrower());
foo_shared(std::shared_ptr<SomeKindofObject>(new SomeKindOfObject()), thrower());
경우 thrower()
예외를 발생 두 라인 메모리 누출. 그리고 그 이상으로, shared_ptr<>
참조 카운트가 가리키는 객체에서 멀리 떨어져 있으며 이는 두 번째 할당을 의미 할 수 있습니다 ). 일반적으로 바람직하지 않습니다.
이 문제를 해결하기 위해 C ++ 11이 제공 std::make_shared<>()
하고 C ++ 14가 제공 std::make_unique<>()
합니다.
foo_unique(std::make_unique<SomeKindofObject>(), thrower());
foo_shared(std::make_shared<SomeKindofObject>(), thrower());
이제 두 경우 모두 thrower()
예외가 발생 하더라도 메모리 누수가 발생하지 않습니다. 보너스로, make_shared<>()
그 참조 카운트를 만들 수있는 기회가 동일한 메모리 공간에서 당신에게 예외 안전 보장을 제공하면서, 모두 빠르게 할 수 있고 메모리의 몇 바이트를 저장할 수 있습니다 관리되는 객체로를!
Qt에 대한 메모
그러나 C ++ 11 이전의 컴파일러를 지원해야하는 Qt에는 자체 가비지 수집 모델이 있습니다. 많은 사람들 QObject
이 사용자가 필요로하지 않고 제대로 파괴되는 메커니즘을 가지고 delete
있습니다.
QObject
C ++ 11 관리 포인터로 관리 할 때 s가 어떻게 작동 하는지 알지 못 하므로 shared_ptr<QDialog>
좋은 생각 이라고 말할 수 없습니다 . Qt에 대한 충분한 경험이 없지만 Qt5 가이 사용 사례에 맞게 조정 되었다고 생각 합니다.