2010-04-02 3 views
3

Это становится общей моделью в моем коде, поскольку, когда мне нужно управлять объектом, который должен быть неготовным, потому что либо он «тяжелый», либо B. это ресурс операционной системы, такой как критический раздел:Есть ли способ повысить эффективность shared_ptr, сохранив счетчик ссылок внутри контролируемого объекта?

class Resource; 

class Implementation : public boost::noncopyable 
{ 
    friend class Resource; 
    HANDLE someData; 
    Implementation(HANDLE input) : someData(input) {}; 
    void SomeMethodThatActsOnHandle() { 
     //Do stuff 
    }; 
public: 
    ~Implementation() { FreeHandle(someData) }; 
}; 

class Resource 
{ 
    boost::shared_ptr<Implementation> impl; 
public: 
    Resource(int argA) explicit { 
     HANDLE handle = 
      SomeLegacyCApiThatMakesSomething(argA); 
     if (handle == INVALID_HANDLE_VALUE) 
      throw SomeTypeOfException(); 
     impl.reset(new Implementation(handle)); 
    }; 
    void SomeMethodThatActsOnTheResource() { 
     impl->SomeMethodThatActsOnTheHandle(); 
    }; 
}; 

Таким образом, shared_ptr заботится о подсчете ссылок головных болях, позволяя Resource быть копируемыми, даже если основная ручка должна быть закрыта только один раз все ссылки на него будут уничтожены.

Однако, похоже, что мы могли бы сэкономить накладные расходы на выделение ссылочных счетчиков shared_ptr и такое отдельно, если бы мы могли каким-то образом перемещать эти данные внутри Implementation, как это делают интрузивные контейнеры boost.

Если это делает преждевременную оптимизацию, взломать некоторых людей, я действительно согласен с тем, что мне это не нужно для моего текущего проекта. Но мне любопытно, если это возможно.

+0

У Boost есть 'intrusive_ptr': http://www.boost.org/doc/libs/1_42_0/libs/smart_ptr/intrusive_ptr.html Но я не отправляю в качестве ответа, потому что я не уверен, что это что ты хочешь. – GManNickG

+1

Кроме того, ЭТО ПРОДОЛЖИТЕЛЬНАЯ ОПТИМИЗАЦИЯ ТЕМПЕРАТУРЫ WTF ВЫ ДЕЛАЕТЕ? Разве ты не знаешь о ЦИТАТЕ КУНТА? ОПТИМИЗАЦИЯ ПРАКТИКИ - КОРН ВСЕГО ЗЛА? Хм, ты не слышал, что это? ТЕПЕРЬ У ВАС ЕСТЬ. – GManNickG

+1

@GMan: LOL! Хотя читатели этого читают, я считаю, что цитата: «Мы должны забыть о небольшой эффективности, скажем, около 97% времени: преждевременная оптимизация - это корень всего зла». Я утверждаю, что это 3% -ый случай. –

ответ

6

Частичное решение заключается в использовании make_shared для создания shared_ptr. Например,

auto my_thing = std::make_shared<Thing>(); 

вместо

auto my_thing = std::shared_ptr<Thing>(new Thing); 

Это еще ненавязчивый, так что ничего не нужно менять. Хорошие реализации make_shared объединяют выделение памяти для счетчика ссылок и самого объекта. Это экономит выделение памяти и держит счетчик близко к объекту для лучшей локальности. Это не так эффективно, как что-то вроде boost:intrusive_ptr, но это стоит рассмотреть.

+0

Я не могу этого сделать, потому что общий указатель сконструирован как член 'Resource', прежде чем я построил аргумент, необходимый для' Реализация'. В итоге вы создаете shared_ptr для NULL, а затем назначаете ему новый shared_ptr, выделенный с помощью make_shared. Стоимость распределения уже была оплачена вызовом impl перед тем, как конструктор начал выполнение. –

+2

@BillyIneal: По умолчанию shared_ptr не должен выделять ничего, пока не будет вызван сброс. Это должно быть очень дешево. – dvide

6

Используйте boost::intrusive_ptr, который предназначен для работы с классом со встроенным подсчетом ссылок.

Non проверенного примера, основанная на example here:

class Resource; 

class Implementation : public boost::noncopyable 
{ 
    friend class Resource; 
    HANDLE someData; 
    int refCount; // The reference count. 
    Implementation(HANDLE input) : someData(input) { refCount = 0; }; 
    void SomeMethodThatActsOnHandle() { 
     //Do stuff 
    }; 
public: 
    ~Implementation() { FreeHandle(someData) }; 
}; 

intrusive_ptr_add_ref(Implementation* imp) { imp->refCount++; } 
intrusive_ptr_release(Implementation* imp) { if(--imp->refCount) == 0) delete imp; } 

class Resource 
{ 
    boost::intrusive_ptr<Implementation> impl; 
public: 
    Resource(int argA) explicit { 
     HANDLE handle = 
      SomeLegacyCApiThatMakesSomething(argA); 
     if (handle == INVALID_HANDLE_VALUE) 
      throw SomeTypeOfException(); 
     impl.reset(new Implementation(handle)); 
    }; 
    void SomeMethodThatActsOnTheResource() { 
     impl->SomeMethodThatActsOnTheHandle(); 
    }; 
}; 
+0

Можете ли вы привести пример (желательно выше, один из них отредактирован), который использует 'intrusive_ptr '? Я видел это в документах, но документация немного непрозрачна ... –

+0

О, и +1. |||| –

+2

Не забудьте сделать атомы приращения и уменьшения и испытания. – Permaquid

1

вы можете сохранить некоторые накладные расходы, просто избавившись от двух классов и имеющей только один и typedefing общего PTR к нему - это IDOM я использую все время

class Resource 
{ 
     ... 
}; 
typedef boost::shared_ptr<Resource> ResourcePtr; 
+0

Это все еще требует от пользователей выбора, следует ли использовать вариант указателя или нет. Я бы предпочел избежать этого. Плюс он не отвечает на вопрос, который я задал. –

1

Если вы хотите, чтобы уменьшить накладные расходы на отдельные выделения памяти для вашего объекта и эталонного счетчика, вы можете попробовать использовать make_shared. Вот для чего это.

+0

Я не могу этого сделать, потому что shared_ptr построен до того, как я знаю, что ему назначить в моем конструкторе. –

+0

@BillyIneal: Итак? Я не понимаю, почему make_shared не применим. Создание shared_ptr, который указывает на ничего, не требует счетчика ref. – sellibitze

+0

Я думаю, что это было бы так же эффективно, как использование 'intrusive_ptr'. 'intrusive_ptr' хорош для управления ресурсами, у которых уже есть счетчик ссылок, например COM-объекты. Но я не думаю, что многое можно получить, явно кодируя подсчет ссылок, когда он не нужен. Как уже упоминалось, 'make_shared' должен выделять счетчик ссылок и' Реализация' за один раз, поэтому этот шаг должен быть ничем не отличающимся от новички «Реализация», которая содержит счетчик ссылок внутри себя. – dvide