У меня есть общий объект, который нужно отправить в системный API и извлечь его позже. Системный API получает только void *. Я не могу использовать shared_ptr :: get (), потому что он не увеличивает счетчик ссылок и может быть выпущен другими потоками перед извлечением из системного API. Отправка нового shared_ptr * будет работать, но требует дополнительного выделения кучи.
Один из способов сделать это - разрешить объекту, производному от enable_shared_from_this. Однако, поскольку этот шаблон класса владеет только weak_ptr, этого недостаточно для предотвращения освобождения объекта.
Итак, мое решение выглядит следующим образом:
class MyClass:public enable_shared_from_this<MyClass> {
private:
shared_ptr<MyClass> m_this;
public:
void *lock(){
m_this=shared_from_this();
return this;
}
static shared_ptr<MyClass> unlock(void *p){
auto pthis = static_cast<MyClass *>(p);
return move(pthis->m_this);
}
/* ... */
}
/* ... */
autp pobj = make_shared<MyObject>(...);
/* ... */
system_api_send_obj(pobj->lock());
/* ... */
auto punlocked = MyClass::unlock(system_api_reveive_obj());
Есть ли более простой способ сделать это?
Обратной стороной этого решения:
требуется дополнительный shared_ptr
в макете объекта MyClass в дополнение к weak_ptr
в базовом классе enable_shared_from_this
.
Как я уже упоминал в комментариях, одновременный доступ к lock ()
и unlock ()
НЕ является безопасным.
Хуже всего то, что это решение может поддерживать lock ()
только один раз перед вызовом unlock ()
. Если один и тот же объект должен использоваться для нескольких вызовов системного API, необходимо реализовать дополнительный подсчет ссылок.
Если у нас есть еще один класс enable_lockable_shared_from_this
, он будет отличным:
class MyClass:public enable_lockable_shared_from_this<MyClass> {
/* ... */
}
/* ... */
autp pobj = make_shared<MyObject>(...);
/* ... */
system_api_send_obj(pobj.lock());
/* ... */
auto punlocked = unlock_shared<MyClass>(system_api_reveive_obj());
И реализация enable_lockable_shared_from_this
аналогична enable_shared_from_this]
реализует lock ()
и вспомогательную функцию unlock_shared
. Вызов этих функций только явно увеличивает и уменьшает use_count (). Это будет идеальным решением, потому что:
Это устраняет дополнительные затраты на пространство.
Он повторно использует возможности, существующие для shared_ptr, чтобы гарантировать безопасность параллелизма.
Лучшее в этом решении - то, что оно поддерживает множественные вызовы lock ()
без проблем.
Однако единственный самый большой недостаток: в данный момент он недоступен!
ОБНОВЛЕНИЕ:
По крайней мере, два ответа на этот вопрос связаны с контейнером указателей.Пожалуйста, сравните эти решения со следующим:
class MyClass:public enable_shared_from_this<MyClass> {
private:
shared_ptr<MyClass> m_this;
mutex this_lock; //not necessory for single threading environment
int lock_count;
public:
void *lock(){
lock_guard lck(this_lock); //not necessory for single threading environment
if(!lock_count==0)
m_this=shared_from_this();
return this;
}
static shared_ptr<MyClass> unlock(void *p){
lock_guard lck(this_lock); //not necessory for single threading environment
auto pthis = static_cast<MyClass *>(p);
if(--lock_count>0)
return pthis->m_this;
else {
lock_count=0;
return move(pthis->m_this); //returns nullptr if not previously locked
}
}
/* ... */
}
/* ... */
autp pobj = make_shared<MyObject>(...);
/* ... */
system_api_send_obj(pobj->lock());
/* ... */
auto punlocked = MyClass::unlock(system_api_reveive_obj());
Это абсолютно игра O (1) против O (n) (пространство; время - O (log (n)) или аналогичная, но абсолютно большая, чем O (1)).