std::shared_ptr<B> psharedB = std::make_shared<B>();
auto t1 = std::thread(&B::bar,psharedB);
Я не адвокат по языку, но я думаю, что приведенный выше код создаст экземпляр класса B
, на который указывает общий указатель psharedB
, а затем создаст экземплярstd::thread
передача по значению указатель функции на метод B::bar
в качестве первого параметра и передача по значению a копирование из psharedB
в качестве второгопараметр.
Поскольку копия psharedB
передается конструктору thread ( … )
, количество ссылок общего указателя увеличивается до двух.Вы можете исследовать это, добавив следующий код в threadTask
, чтобы проверить значение psharedB.use_count()
после создания нового потока…
auto t1 = std::thread(&B::bar, psharedB);
// show use count is two not one
std::cout << "psharedB use count: " << psharedB.use_count() << std::endl;
… документация предупреждает, что значение, возвращаемое use_count равно "«приблизительно» в многопоточной среде, но в данном конкретном случае это должно быть точно.
Когда threadtask
выходит, psharedB
выходит за рамки.Счетчик использования общего указателя уменьшается деструктором общего указателя, но экземпляр объекта B
сохраняется, поскольку копия psharedB
, переданная в поток t1
, все еще существует, поэтому счетчик использования равен единице, а не нулю.
В B::bar
переменная this
представляет собой обычный обычный необработанный указатель B *
.Под обложками он был извлечен из умного указателя, переданного в поток t1
, но что касается логики в B::bar
, мы этого не знаем.Таким образом, когда этот указатель приведен к void *
, он вообще не влияет на счетчик использования общего указателя.
Один из способов изучить это - установить точку останова на reinterpret_cast
в B::bar
и дойти до этой точки.Запись в стеке вызовов непосредственно под точкой останова будет логикой для вызова B::bar
с копией psharedB
.Отладчик должен позволить вам увидеть общий указатель и изучить его счетчик использования, который будет равен единице, если threadTask
уже завершен, или двум, если threadTask
еще не завершен.
Перейдите через reinterpret_cast
и обратите внимание, что счетчик использования общего указателя не меняется.
Когда возвращается B::bar
, поток t1
, запущенный threadTask
, завершается.Фактически копия psharedB
, переданная этому потоку, выходит из области видимости и уничтожается.Счетчик использования общего указателя обнуляется, и экземпляр объекта B
, первоначально созданный threadTask
, также уничтожается.
В этот момент значение ptr
, переданное foo
, является указателем на удаленныйэкземпляр объекта.Любая попытка разыменования этого значения ptr
внутри foo
приведет к неопределенному поведению.Если вам повезет, это вылетит из вашей программы.Если вам не повезло, кажется, что разыменование работает, и вам будет сложно отследить сбой позже при выполнении вашей программы.Или какое-то странное поведение, которое случается иногда и очень трудно воспроизвести.
Еще хуже, в зависимости от , когда поток t1
уничтожен, и, таким образом, , когда B
Экземпляр объекта уничтожен, переменная ptr
в foo
может быть действительным указателем B*
для некоторых из foo
исполнений и недопустимым указателем для остальных из foo
.
Допустимо использовать shared_from_this и привести его к void *?
Ваши классы A
и B
не являются производными от std::enable_shared_from_this<T>
, поэтому в вашемНапример, нельзя вызывать std::shared_from_this
в любом классе.
Если вы действительно создали shared_ptr<B>
экземпляр, вы обнаружите, что не можете привести его к void *
.Попробуйте добавить auto temp = reinterpret_cast<void *> (psharedB);
к threadTask
.Вы обнаружите ошибку компилятора, потому что приведение является недопустимым преобразованием.
Допустимо ли использовать это в функции, которая вызывается с использованием общего указателя
Да, это действительно - в рамках определенных ограничений.Ваш метод B::bar
, вызванный из threadTask
, является функцией, которая вызывается с использованием общего указателя.Вы можете использовать this
в пределах B::bar
.Но вы не получите никакой специальной возможности для this
только потому, что она была вызвана с помощью общего указателя.Что касается B::bar
, то он мог быть вызван с использованием необработанного указателя, подобного этому ...
B* p = new B();
p->bar();
Используете ли вы совместно используемый указатель или необработанный указатель - недопустимо передавать this
или void *
версию this
в другую функцию, например A::foo
, без гарантии того, что экземпляр указывает на указательЗначение по-прежнему будет действительным, когда A::foo
разыменует этот указатель.
Если вы используете shared_ptr<T>
для управления сроком службы объекта, то в идеале вы хотите передать A::foo
параметр shared_ptr<B>
.Таким образом, объект поддерживается живым с помощью shared_ptr
, переданного A::foo
, даже если все другие общие указатели уничтожены.