Cyclic Dependency Problem

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • scruggsy
    New Member
    • Mar 2007
    • 147

    Cyclic Dependency Problem

    Hi, got a possibly stupid question. I have 2 header files each containing class definitions along the lines of:
    Code:
    // Header1.h
    #pragma once
    #include "Header2.h" // for Class2
    
    class ClassA
    {
      Class2 m_class1;
    };
    
    class ClassB
    {
      int whatever;
    };
    Code:
    // Header2.h
    #pragma once
    #include "Header1.h"  // for ClassB
    
    class Class1
    {
      ClassB m_classB;
    }
    
    class Class2
    {
      int someThing;
    };
    Merging the files together isn't possible. Changing the class definitions isn't possible (I am using them but did not write them).
    How can I get this to compile?
  • boxfish
    Recognized Expert Contributor
    • Mar 2008
    • 469

    #2
    Two words: Forward Declaration.
    You can let the compiler know about ClassB and Class2 before it sees them. Just use the statements
    class ClassB;
    and
    class Class2;
    Then you can use the names ClassB and Class2 before they are defined.

    So:

    Code:
    // Header1.h
    #pragma once
    #include "Header2.h" // for Class2
    
    class Class2;
    
    class ClassA
    {
    Class2 m_class1;
    };
    
    class ClassB
    {
    int whatever;
    };
    And:

    Code:
    // Header2.h
    #pragma once
    #include "Header1.h" // for ClassB
    
    class ClassB;
    
    class Class1
    {
    ClassB m_classB;
    }
    
    class Class2
    {
    int someThing;
    };
    Hope this helps.

    Comment

    • scruggsy
      New Member
      • Mar 2007
      • 147

      #3
      Originally posted by boxfish
      Two words: Forward Declaration.
      I appreciate that, but it doesn't help in this instance because each class contains an object of a class defined in the other file rather than just a pointer to such an object, so the compiler needs to know what the contained object looks like in order to construct the containing class.

      I managed to work around it in this case using a forward declaration due to the way one of the classes is laid out (class1 actually looks more like this):
      Code:
       class Class1
       {
      struct Data {
       ClassB m_classB;
        int someMoreData;
      };
      
      Data * data;
       };
      I can redefine data as a pointer to a ClassB object and do some ugly pointer arithmetic to get at the someMoreData member of the struct, which works mainly because I don't need to create objects of Class1, just interface with them.
      But I expect this kind of issue comes up occassionally and I am curious how people solve it.

      Comment

      • Banfa
        Recognized Expert Expert
        • Feb 2006
        • 9067

        #4
        Originally posted by scruggsy
        But I expect this kind of issue comes up occassionally and I am curious how people solve it.
        That is very poor class design and/or file design (the problem wouldn't occur if you stuck to the simple rule 1 class per file pair (*.h, *.cpp).

        There is no way round it without either altering one of the classes to make forward declaration possible or re-organising the filesso there are more containing less classes.

        Comment

        • weaknessforcats
          Recognized Expert Expert
          • Mar 2007
          • 9214

          #5
          The only way a forward declaration works is if the class has a pointer to the other class.

          If the class contains an object of another class, the forward declaration doesn't work becuse the compiler needs to see of there is a default constructor or not.

          Comment

          • donbock
            Recognized Expert Top Contributor
            • Mar 2008
            • 2427

            #6
            Define a third header file that contains all of the intertwined declarations from the original headers. Then modify the original header files to include that new file.

            The header contents you supplied suggest that there is a single logical entity that encompasses some of the features in each of the original files. This new third header file I'm suggesting is for that logical entity.

            Intertwined headers like this suggest a problem in the design that led to this file partitioning. You might want to look up the terms "coupling" and "cohesion" as used to describe properties of a software design.

            By the way, don't forget to make your header files idempotent. That is, no errors should be provoked by including them more than once.

            Cheers,
            donbock

            Comment

            • scruggsy
              New Member
              • Mar 2007
              • 147

              #7
              Originally posted by donbock
              Define a third header file that contains all of the intertwined declarations from the original headers. Then modify the original header files to include that new file.

              The header contents you supplied suggest that there is a single logical entity that encompasses some of the features in each of the original files. This new third header file I'm suggesting is for that logical entity.

              Intertwined headers like this suggest a problem in the design that led to this file partitioning. You might want to look up the terms "coupling" and "cohesion" as used to describe properties of a software design.

              By the way, don't forget to make your header files idempotent. That is, no errors should be provoked by including them more than once.

              Cheers,
              donbock
              Thanks, I think that should be doable although it's a bit of a change from how we have been organizing the class definitions. I suppose this issue would be less likely to come up if we were designing from scratch rather than documenting someone else's classes.

              Comment

              Working...