2010-06-21 22 views
8

El objeto raíz del MFC El constructor y la asignación de copia de CObject están deshabilitados de manera predeterminada.Por qué deshabilitar la asignación y el constructor de copia de CObject

El estándar C++ copia clase predeterminada constructor hace un copia miembro por miembro. La presencia del constructor de copia privada CObject garantiza un mensaje de error del compilador si se necesita constructor de su clase pero no está disponible. Por lo tanto, debe proporcionar un constructor de copia si su clase requiere esta capacidad.

  • En el código fuente de CObject, hay un comentario:

deshabilitar el constructor de copia y asignación por defecto por lo que obtendrá errores de compilación en lugar de un comportamiento inesperado si se pasa objetos por valorar o asignar objetos.

Mi pregunta es, ¿cuál es el problema con el constructor de copiado bit por bit predeterminado para esta clase CObject? En mi opinión, sería mejor para darnos el constructor de copia por defecto, y que podría proporcionar una si es necesario (en profundidad)

+0

mejor quite la etiqueta MFC para atraer a más usuarios –

+1

Sin embargo, la respuesta probablemente sea específica de MFC. – peterchen

+0

¡Gracias, funciona! –

Respuesta

6

El valor predeterminado es miembro por miembro, no a nivel de bit.

La mayoría de las clases derivadas de CObject contienen y gestionan directamente algunos recursos del sistema que no tienen recuento de referencias ni mecanismos similares, por lo que la elección probablemente se haya realizado teniendo en cuenta el uso predeterminado.

p. Ej. CGdiObject es más o menos:

class CGDIObject : public CObject 
{ 
    HGDIOBJ m_handle; 

    CGDIObject() : m_handle(0) {} 
    // derived classes provide a create, attach etc. 
    ~CGDIObject() { DeleteObject(m_handle); } 
} 

El constructor de copia por defecto en este caso sería peligroso (que conduce a la destrucción doble), proporcionando un "correcta" constructor de copia es sorprendentemente difícil y costoso.

Otra razón puede ser que la mayoría de las clases derivadas de CObject están destinadas a ser mutadas, y por lo tanto pasan por referencia.Un constructor de copia faltante cogerá copias no deseadas que mutan una copia en lugar del objeto pasó:

class CMyObject : public CObject 
{ 
    public: 
     AttachFoo(FooHandle foo) { ... } 
     AddBar() { ... } 
}; 

bool InitMySession(CMyObject & obj) 
{ 
    obj.AttachFoo(CreateRawFoo()); 
    obj.AddBar(); 
    obj.AddBar(); 
} 

// ... 
CMyObj mo; 
InitMySession(mo); 

La omisión de la "&" le da un código que compila bien, pero crea una copia temporal, modifica eso, y luego deja caer , mientras que mo permanece sin modificar.

Muchas API siguen ese patrón, ya que MFC no se basa en excepciones para el manejo de errores (por razones históricas: no todos los compiladores específicos los soportan bien, y MFC requiere una gran cantidad de recursos adicionales que se vuelven dolorosos con excepciones)


No creo que estas opciones sean buenas, p. las clases derivadas deberían poder utilizar el constructor de copia predeterminado si sus miembros lo permiten (y la mayoría de los miembros deberían permitirlo).

La decisión se ajusta a la "mentalidad" de MFC, sin embargo, y los requisitos/restricciones de la hora en que se creó MFC.

+0

buen punto, la estrecha relación entre MFC y los recursos del sistema puede permitirles decidir hacerlo. No entendí bien tu segundo punto, ¿qué quieres decir cuando hablas de mutado? –

+0

agregó más detalles – peterchen

3

Considere lo siguiente:

class CMyHandle : public CObject 
{ 
    HANDLE hWin32; 
public: 
    CMyHandle() 
    { 
     hWin32 = SomeFunctionThatMakesHandle(); 
    } 
    ~CMyHandle() 
    { 
     CloseHandle(hWin32); 
    } 
}; 

Ahora, si copia CMyHandle, la handle termina siendo cerrado dos veces, y después de que cualquiera de las instancias CMyHandle se destruye, la otra instancia deja de ser válida.

Dado que una gran cantidad de clases de MFC administran identificadores, tiene sentido obligar al creador de la clase a anular explícitamente para crear constructores de copia y copiar operadores de asignación.

EDIT: Por ejemplo: constructor de copia

int main() 
{ 
    CMyHandle h1; //CMyHandle's constructor initializes the handle 
    { 
     CMyHandle h2(h1); //Memberwise copy into h2. In this case, it copies the 
          //handle 
    } //h2 destroyed, closes handle 
    //h1 is now invalid (because the underlying handle was closed)! 
} 
Cuestiones relacionadas