У меня есть общий объект, который необходимо отправить системному API и извлечь его позже.Системный API получает только void *.Я не могу использовать shared_ptr :: get (), потому что он не увеличивает счетчик ссылок и может быть освобожден другими потоками перед извлечением из системного API.Отправка нового shared_ptr * будет работать, но требует дополнительного выделения кучи.
Один из способов сделать это - позволить объекту, полученному из enable_shared_from_this.Однако, поскольку этот шаблон класса владеет только слабым_птром, этого недостаточно для того, чтобы не допустить освобождения объекта.
Поэтому мое решение выглядит следующим образом:
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>
в макете объекта 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)) игра.