Я работал над заменой необработанных указателей указателями с подсчетом ссылок, которые предоставляют только константную версию базового. Моя цель - уменьшить использование памяти (и время, затрачиваемое на ненужное создание и уничтожение сложных объектов), не оказываясь в ситуации, когда какой-либо код имеет доступ к памяти, которой он не владеет. Мне известно о проблеме циклических ссылок со счетчиком ссылок, но мой код никогда не должен создавать такую ситуацию.
Требование константности работает, потому что я использую систему, в которой классы обычно не предоставляют неконстантных членов, и вместо того, чтобы изменять объект, вы должны вызвать для него метод, который возвращает новый объект, полученный в результате изменения. Это, вероятно, шаблон дизайна, но я не знаю его по имени.
Моя проблема возникла, когда у меня есть метод, который возвращает указатель на объект его типа, который иногда сам по себе. Ранее это выглядело примерно так:
Foo * Foo::GetAfterModification( const Modification & mod ) const
{
if( ChangesAnything( mod ) )
{
Foo * asdf = new Foo;
asdf.DoModification( mod );
return asdf;
}
else
return this;
}
Я не могу найти хороший способ сделать это возвращение умный указатель. Наивный подход был бы чем-то вроде return CRcPtr< Foo >( this )
, но это нарушает семантику владения, потому что то, что я возвращаю и кто раньше владел объектом, теперь каждый думает, что он владеет, но не знает друг о друге. Единственный безопасный способ сделать это - return CRcPtr< Foo >( new Foo( *this ) )
, но это противоречит моему намерению ограничить ненужное использование памяти.
Есть ли способ безопасно вернуть умный указатель без выделения дополнительной памяти? Я подозреваю, что нет. Если бы было, как бы это работало, если бы объект был размещен в стеке? Этот вопрос кажется связанным, но он не тот, потому что он может просто заставить свои функции принимать необработанные указатели в качестве параметров и потому, что он использует библиотеку наддува.
Для справки, моя домашняя реализация умного указателя приведена ниже. Я уверен, что он может быть более надежным, но он более переносим, чем в зависимости от повсеместного повышения или tr1, и до этой проблемы он работал хорошо для меня.
template <class T>
class CRcPtr
{
public:
explicit CRcPtr( T * p_pBaldPtr )
{
m_pInternal = p_pBaldPtr;
m_iCount = new unsigned short( 1 );
}
CRcPtr( const CRcPtr & p_Other )
{ Acquire( p_Other ); }
template <class U>
explicit CRcPtr( const CRcPtr< U > & p_It )
{
m_pInternal = dynamic_cast< T * >( p_It.m_pInternal );
if( m_pInternal )
{
m_iCount = p_It.m_iCount;
(*m_iCount)++;
}
else
m_iCount = new unsigned short( 1 );
}
~CRcPtr()
{ Release(); }
CRcPtr & operator=( const CRcPtr & p_Other )
{
Release();
Acquire( p_Other );
}
const T & operator*() const
{ return *m_pInternal; }
const T * operator->() const
{ return m_pInternal; }
const T * get() const
{ return m_pInternal; }
private:
void Release()
{
(*m_iCount)--;
if( *m_iCount == 0 )
{
delete m_pInternal;
delete m_iCount;
m_pInternal = 0;
m_iCount = 0;
}
}
void Acquire( const CRcPtr & p_Other )
{
m_pInternal = p_Other.m_pInternal;
m_iCount = p_Other.m_iCount;
(*m_iCount)++;
}
template <class U>
friend class CRcPtr;
T * m_pInternal;
unsigned short * m_iCount;
};
template <class U, class T>
CRcPtr< U > ref_cast( const CRcPtr< T > & p_It )
{ return CRcPtr< U >( p_It ); }
Редактировать: Спасибо за ответы. Я надеялся избежать использования boost или tr1, но я признаю, что не использовать хорошо протестированные библиотеки, как правило, неразумно. Я вполне уверен, что то, что я реализовал, не похоже на std :: auto_ptr, но скорее похоже на tr1 :: shared_ptr, за исключением того, что оно предоставляет только константную версию внутреннего указателя и не имеет некоторых Особенности в официальной версии. Я действительно хотел бы избежать навязчивой схемы, подобной той, которая предложена Джаном Паоло. Я знаю, что моя реализация не является поточно-ориентированной, но это однопоточное приложение.