Dependencias circulares de formularios en C++/CLI

Es una situación relativamente habitual que dos clases se tengan que conocer entre sí, es lo que se conoce como referencia circular. Aunque en principio debería sospechar de esta situación por el alto acoplamiento que introduce entre ambas clases, hay veces, en que puede ser excesivo crear una clase para que maneje la comunicación entre las clases. De todos modos si decidís usar este enfoque, una buena implementación es utilizar el patrón mediator Mediator.

Cuando esto nos ocurre en lenguajes como C# no hay problema, porque no existe el concepto de declaración e implementación de una clase separadas. Pero en C++ toda clase debe conocer la declaración de otra clase que quiera utilizar y el compilador debe ser capaz de encontrar la implementación. El problema es que si A conoce ve y B conoce A cuando el compilador llega a la compilación de la primera de ellas que encuentra en nuestro código no puede saber que es la segunda. Recordemos que la compilación en C++ se hace modulo a modulo y que es el linkado el que pone todo junto. Los errores suelen ser del estilo:

Error 2 error C2065: 'FChild' : undeclared identifier FParent.h 76 
Error 3 error C2065: 'child' : undeclared identifier FParent.h 76 
Error 4 error C2061: syntax error : identifier 'FChild' FParent.h 76 
Error 5 error C2227: left of '->MdiParent' must point to class/struct/union/generic type FParent.h 77 
Error 6 error C2227: left of '->Show' must point to class/struct/union/generic type FParent.h 78
 

El mecanismo habitual para resolver esta situación es utilizar "forward declaration" o declaración adelantada. Hasta aquí sin problema.

#pragma once

#include "B.h"

 

ref class B; //forward declaration

 

ref class A

{

public:

  B^ b;

};

Más sobre "forward declaration" en las siguientes entradas de C++ FAQ Lite:

  • [39.11] How can I create two classes that both know about each other?
  • [39.12] What special considerations are needed when forward declarations are used with member objects?
  • [39.13] What special considerations are needed when forward declarations are used with inline functions? 
  • El problema surge cuando utilizamos C++/CLI y Visual Studio, y queremos comunicar dos formularios, situación habitual fruto de la necesidad de que un formulario se comunique con su padre en interfaces MDI (la propiedad MDIParent devuelve una referencia al padre y el padre suele ser quien crea el formulario hijo) o que el formulario lanzado obtenga datos de su lanzador y viceversa. Son claros casos de referencia circular.

    Bueno pues siguiendo con lo expuesto anteriormente intentamos resolver la referencia circular utilizando "forward declaration" y… NO FUNCIONA!!!!. Obtenemos errores del tipo:

    El problema es que el diseñador de formularios de Visual C++ 2005 solo es capaz de trabajar con clases de formulario definidas en el archivo .h (definiciones inline), si separamos la clase en .h y .cpp, el diseñador deja de funcionar. Luego tenemos que conseguir que funcione la predeclaración usando funciones inline.

    Tal y como se puede leer en la faq [39.13] el orden de las declaraciones es critico cuando usamos "forward declaration" con funciones inline. Esto es fácil si ambas clases se definen e implementan en el mismo archivo .h, pero esto no suele ser viable. Pues bien el truco es cambiar la posición donde se realizan los includes, para que las clases de definan en el orden adecuado:

    /* Archivo A.h */

    #pragma once

     

    ref class B;

     

    ref class A

    {

    public:

      A(void)  {}

     

      void GetB();

    };

     

    #include "B.h"

     

    inline void A::GetB()

    {

      B^ b = gcnew B();

    }

     

    /* Archivo B.h */

    #pragma once

     

    ref class A;

     

    ref class B

    {

    public:

      B(void){}

     

      A^ GetA();

    };

     

    #include "A.h"

     

    inline A^ B::GetA()

    {

        return gcnew A();

    }

    Podéis descargar un ejemplo referencias circulares C++/CLI, basado en WinForms.

    4 comentarios sobre “Dependencias circulares de formularios en C++/CLI”

    1. TENGO UNA PREGUNTA.COMO PUEDO HACER QUE EL FORMULARIO HIJO SEA DE TIPO MODAL? HE VISTO QUE ES IMPOSIBLE USAR SHOWDIALOG

      NECESITO ACCEDER A LAS PROPIEDADES DEL PADRE, Y SI NO LO HAGO CON FORMULARIOS MIDI ES IMPOSIBLE YA QUE NO ME APARECEN ASI LAS PONGA COMO PUBLICAS

      ESPERO SU RESPUESTA GRACIAS

    Deja un comentario

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *