У меня есть пара вопросов для умных указателей, которые раньше я им не давал.
Все началось с попытки понять owner_before, теперь я более запутался, чем раньше, я начал изучать эту тему .. 🙁
Я думаю, что все ваше замешательство исходит от «конструктора псевдонимов»:
template <typename U>
shared_ptr(const shared_ptr<U>& x, element_type* p)
Какая польза от этой вещи? Ну, он редко используется, но он «разделяет» право собственности на некоторый объект x
но когда вы разыграете его, вы получите p
вместо. Это все. Никогда не удалит p
или сделать что-нибудь еще с этим.
Это может быть полезно, если у вас есть что-то вроде этого:
struct Foo {
Bar bar;
};
struct Baz {
Baz(shared_ptr<Bar> bar) : m_bar(bar) {}
shared_ptr<Bar> m_bar;
};
int main()
{
auto foo = make_shared<Foo>();
Baz baz(shared_ptr<Bar>(foo, &foo.bar));
}
Сейчас baz
получает управлять временем жизни foo
не зная, что это делает — он заботится только о том, что он управляет временем жизни Bar
, но так как наши bar
это часть foo
мы не можем уничтожить foo
без разрушения bar
, поэтому мы используем конструктор псевдонимов.
Но на самом деле мы этого не делаем, потому что этот вариант использования очень редкий.
Старый «сырой» указатель:
someType* pR = new someType(param1, param2); //pR is a pointer
MyOwner.TakeOwnershipOf(pR); // Now MyOwner is the owner, **the one who ONLY should call 'delete pR;'**
Умный указатель:
std::shared_ptr<someType> sp = std::make_shared<someType>(param1, param2);
«Зр» теперь владелец (в коде нет «pR», но внутренне это так). И вам не нужно называть «удалить pR». Это объект, который внутренне хранит указатель на pR и удаляет его, когда он больше не нужен.
sp это объект. «sp-> any» точно так же, как «pR-> any». Это может сбить вас с толку о том, что sp также является указателем. Нет, бывает, что «->» перегружен.
Больше перегружено:
sp.get()
такой же как pR
, *sp
такой же как *pR
sp1 == sp2
такой же как pR1 == pR2
и так же, как sp1.get()==sp2.get()
Хорошая вещь О shared_ptr, например, когда вам нужно хранить один и тот же указатель в нескольких контейнерах. Если какой-либо контейнер удален, сохраненный указатель (sp, которому принадлежит pR) также должен быть удален. Это сделает недействительным указатель, хранящийся в другом контейнере. Вместо проверки других контейнеров на предмет наличия этого указателя, shared_ptr берет на себя ответственность за него. Это вы можете безопасно удалить sp
так как pR
будет удален только последним контейнером, который хранит sp
,
Ваше замешательство, потому что владение это не то, что компилятор может проверить; Вы, как программист, должны сделать это.
Мы можем сказать любой объект p
владеет q
если существование p
гарантирует существование q
(желательно без утечек памяти).
Простой случай — прямое владение, где освобождение p
также освободит q
например, если q
является членом p
, или же q
явно освобожден с delete
в p
деструктор.
Умные указатели делают это очевидным для людей. Если q
хранится в std::unique_ptr
член p
, мы знаем это p
владеет q
, Вам не нужно искать (возможно, отсутствует или дублируется) оператор удаления.
Собственность также является переходной, если p
владеет q
а также q
владеет r
, затем p
должен владеть r
,
Если p
напрямую владеет q
и мы хотим создать shared_ptr
который владеет q
то он тоже должен владеть p
, В противном случае, если p
уничтожен, то q
будет тоже, несмотря на существование нашего общего указателя.
Это то, что делает конструктор псевдонимов для std :: shared_ptr (продемонстрировано в ответе Джона).
Расширяется q
продлить p
время жизни, поэтому у нас есть указатель на q
который на самом деле владеет p
, Мы утверждаем компилятору, что p
на самом деле владеет q
так что общий птр транзитивно владеет q
,
Если p
не владеет q
, тогда ваша программа скомпилируется, но она не работает, как если бы вы вручную вызывали delete
дважды.
Для интеллектуальных указателей stl сравнения передаются в необработанный указатель. Таким образом, умные указатели равны, если они обращаются к одному и тому же объекту, а сравнения касаются места в памяти. Не существует каких-либо определенных поведений, определяющих области памяти, поэтому вы не можете использовать его для чего-то другого, кроме хранения в map
или же set
,