На самом деле, я тоже столкнулся с этой проблемой сегодня.Не желая принимать «не может быть сделано» и «использовать shared_ptr / подсчет ссылок», прибегая к помощи, я придумал этот базовый класс:
class Resource
{
private:
mutable bool m_mine;
protected:
Resource()
: m_mine( true )
{
}
Resource(const Resource&) = delete;
void operator=(const Resource&) = delete;
Resource(const Resource&& other)
: m_mine( other.m_mine )
{
other.m_mine = false;
}
bool isMine() const
{
return m_mine;
}
};
Все методы и конструкторы защищены, вам нужнонаследовать от него, чтобы использовать его.Обратите внимание на изменяемое поле: это означает, что потомок может быть константным членом в классе.Например,
class A : protected Resource
{
private:
const int m_i;
public:
A()
: m_i( 0 )
{
}
A( const int i )
: m_i( i )
{
}
A(const A&& a)
: Resource( std::move( a ) )
, m_i ( std::move( a.m_i ) ) // this is a move iff member has const move constructor, copy otherwise
{
}
~A()
{
if ( isMine() )
{
// Free up resources. Executed only for non-moved objects
cout << "A destructed" << endl;
}
}
};
Поле (я) поля A теперь может быть постоянным.Обратите внимание, что я унаследовал защищенный, так что пользователь не может случайно привести A к Ресурсу (или очень охотно взломать его), но A все еще не является окончательным, поэтому вы все еще можете наследовать от него (действительная причина наследовать от Resourceнапример, иметь отдельный доступ для чтения и чтения-записи).Это один из крайне редких случаев, когда защищенное наследование не означает автоматически, что ваш дизайн неисправен;однако, если вам трудно это понять, вы можете просто использовать публичное наследование.
Затем, если у вас есть struct X
:
struct B
{
const A m_a;
const X m_x;
B(const A&& a, const X& x) // implement this way only if X has copy constructor; otherwise do for 'x' like we do for 'a'
: m_a( std::move( a ) )
, m_x( x )
{
}
B( const B&& b )
: m_a( std::move( b.m_a ) )
, m_x( std::move( b.m_x ) ) // this is a move iff X has move constructor, copy otherwise
{
}
~B()
{
cout << "B destructed" << endl;
}
};
Обратите внимание, что поля B также могут бытьУст.Наши конструкторы ходов постоянны.Если у ваших типов есть соответствующие конструкторы перемещения, любая выделенная куча память может быть распределена между объектами.