c++11 raii move-constructor

c++11 - Mover constructor y const miembros variables



raii move-constructor (5)

Me gusta la idea de las variables miembro const, especialmente cuando envuelvo las funciones C en clases. El constructor toma un identificador de recursos (por ejemplo, un descriptor de archivo) que permanece válido durante toda la vida útil del objeto y el destructor finalmente lo cierra. (Esa es la idea detrás de RAII, ¿verdad?)

Pero con el constructor de movimiento C ++ 0x me encuentro con un problema. Dado que también se llama al destructor en el objeto "descargado", necesito evitar la limpieza del controlador de recursos. Dado que la variable miembro es constante, no tengo forma de asignar el valor -1 o INVALID_HANDLE (o valores equivalentes) para indicar al destructor que no debe hacer nada.

¿Hay alguna forma de que no se llame al destructor si el estado de un objeto se movió a otro objeto?

Ejemplo:

class File { public: // Kind of "named constructor" or "static factory method" static File open(const char *fileName, const char *modes) { FILE *handle = fopen(fileName, modes); return File(handle); } private: FILE * const handle; public: File(FILE *handle) : handle(handle) { } ~File() { fclose(handle); } File(File &&other) : handle(other.handle) { // The compiler should not call the destructor of the "other" // object. } File(const File &other) = delete; File &operator =(const File &other) = delete; };


El conteo de referencias es un enfoque estándar que resuelve su problema. Considere agregar referencias a su clase; ya sea manualmente, o utilizando herramientas existentes como boost shared_ptr.


En realidad, yo también me he encontrado con este problema hoy. No estoy dispuesto a aceptar ''no se puede hacer'' & ''uso de shared_ptr / reference counting'', busqué más en Google y se me ocurrió esta clase base:

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; } };

Todos los métodos y constructores están protegidos, necesita heredarlos para usarlos. Observe el campo mutable: esto significa que el descendiente puede ser un miembro constante en una clase. P.ej,

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; } } };

Los campos de A pueden ser constantes ahora. Tenga en cuenta que he heredado protegido, por lo que el usuario no puede lanzar A a un recurso por accidente (o está dispuesto a piratearlo), pero A aún no es definitivo, por lo que aún puede heredar de esto (una razón válida para heredar de un recurso es por ejemplo, para tener acceso separado de lectura y lectura-escritura). Este es uno de los casos extremadamente raros en los que la herencia protegida no significa automáticamente que su diseño sea defectuoso; sin embargo, si le resulta difícil de entender, puede utilizar la herencia pública.

Entonces, asumiendo que tienes una 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; } };

Tenga en cuenta que los campos de B también pueden ser const. Nuestros constructores de movimiento son const. Dado que sus tipos tienen constructores de movimiento adecuados, cualquier memoria asignada en el montón se puede compartir entre objetos.


Es por esto que no debe declarar const dichas variables miembro. const variables de miembros const general no sirven para nada. Si no desea que los usuarios muten el FILE* , no les proporcione funciones para que lo hagan, y si desea evitar mutilarlo por accidente, marque las funciones const . Sin embargo, no haga que las variables miembro sean const , ya que se encontrará con la diversión cuando comience a usar mover o copiar semántica.


La forma típica de implementar un constructor de movimientos es poner a cero o, de lo contrario, invalidar a los miembros de la instancia que se está moviendo (consulte MSDN para ver un ejemplo sencillo). Por lo tanto, yo diría que simplemente no use const , ya que es incompatible con los objetivos de la semántica de movimientos.


No, no hay manera de hacer esto. Yo sugeriría que si usted está realmente vinculado a la variable de handle , debe tener una variable de miembro de bandera no constante que indique si la destrucción debe hacer algo o no.