У меня есть общий объект, который необходимо отправить в системный API и извлечь его позже. Системный API получает только void *. Я не могу использовать shared_ptr:: get(), потому что он не увеличивает счетчик ссылок и может быть выпущен другими потоками перед извлечением из системного API. Отправка нового shared_ptr * будет работать, но включает в себя дополнительное распределение кучи.
Один из способов сделать это - позволить объекту, полученному из enable_shared_from_this. Однако, поскольку этот шаблон класса имеет только слабый_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());
Есть ли более простой способ сделать это?
Недостаток этого решения:
-
для макета объекта MyClass требуется дополнительно
shared_ptr<MyClass>
, кромеweak_ptr
в базовом классеenable_shared_from_this
. -
Как я упоминал в комментариях, доступ к
lock()
иunlock()
одновременно НЕ БЕЗОПАСНЫЙ. -
Хуже всего то, что это решение поддерживает только
lock()
один раз перед вызовомunlock()
. Если один и тот же объект должен использоваться для нескольких вызовов API системы, необходимо выполнить дополнительный подсчет ссылок.
Если у нас есть другой класс enable_lockable_shared_from_this
, он будет greate:
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, гарантирует безопасность concurrency.
-
Лучшее в этом решении состоит в том, что он поддерживает множество вызовов
lock()
.
Однако, самый большой недостаток: он недоступен на данный момент!
UPDATE:
Как минимум два ответа на этот вопрос включают контейнер указателей. Пожалуйста, сравните эти решения со следующим:
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) vs O (n) (пространство, время O (log (n)) или аналогичное, но абсолютно большее, чем O (1)) игра.