C++ header files and forward declarations

Anybody either have some good tips, or point me to some sources with good tips for avoiding problems with header file includes when classes reference each other, i.e. class A has a member variable (or reference or pointer to B) and B has a member variable (or reference or pointer to A)

Forward declaration tips would also be welcome!

Thanks.


Answer this question

C++ header files and forward declarations

  • Bryant Cobarrubias - DocEdge

    Caveat(s):

    Kind of ugly, and defeats the purpose of strong language typing.
    Runtime execution

    TestProg
    ------------------------------------------------------------------------------
    #include <iostream>

    using namespace std;

    // Quick enum to identify which ugly type you're looking at

    enum AnEnum { Type1, Type2, Typen };

    // A common base object is required for everybody

    class BaseClass
    {
    public:
      BaseClass(AnEnum aType) : TheType(aType) {} // Constructor initialization rqd
      AnEnum TheType; // Identify what you are
    };

    // Object 1

    class A : public BaseClass // Make me a common object
    {
    public:
      A() : BaseClass(Type1) {} // This is object 1
      BaseClass* AnotherClass;  // Forward declaration --- bad child
    };

    class B : public BaseClass // Make me a common object
    {
      B() : BaseClass(Type2) {} // This is an object 2
      BaseClass* AnotherClass; // Forward declaration -- bad child
    };

    int main()
    {
      A a;
      B b;
      b.AnotherClass = reinterpret_cast<BaseClass*>(&a); // Blind b ptr to a.

      return 0;
    }
    ------------------------------------------------------------------------------
    cl -Zi -EHsc TestProg.cpp
    ------------------------------------------------------------------------------

    I know this is kind of awful, but in comparison to patching in virtual methods
    with the same types of casts all over the place, it works reasonably well.

    I've successfully used this approach for queing anonymous objects from
    UI elements with callback through database search acceleration. (Recursive
    indexed data element search).

    Hope it helps

    itsmike



  • gkovan

     itsmike wrote:

    t.VoidFcn = dynamic_cast<Void_Action>(&test::fcn);

    Odd. That should not work and I'm disappointed that whatever version you are using of MSVC++ allows it.

    The target type of a dynamic_cast can only be a pointer or reference to a class type, or must be a pointer to cv-qualified or unqualified void (never a pointer to a function or a pointer to a member function), and the operand can only be an rvalue pointer to a class type or an lvalue of a class type. As well, function pointers and member function pointers can't ever be operands of dynamic_cast. Your compiler should be giving you an error message, otherwise it is noncompliant with the standard.

  • SujeeKes

    Rivorus,

    You hit the nail on the head by providing a nice rule of thumb for the include vs forward declaration issue.

    Thanks!

    I've been away from C++ for a while (doing Java) so I'm rusty on a few things.  May I ask Another question

    I have a call to strcpy( ) in my main window class of an MFC app.
    the call to the function is copying a const char* argument pased to the function containing the call to strcpy( ) to a char* member variable of the main window class.  My intention is to draw the text on the client area of the main window with a call to DrawText( ) from the main window OnPaint function using the member variable char*.

    The problem I'm having is the call to strcpy( ) is blowing up.  I'm getting an unhandled exception, I beleive a memory access violation (could be something else but I don't have access to the IDE right now!).

    Could you provide any help

    Thanks,
    Mike

  • Doc N Daisy Books

    Looks like a compiler issue has been resolved. Older compiler required
    a reinterpret_cast when anonymously storing method pointers. This MS version
    will allow (and correctly execute) using dynamic_cast.

    eg.

    t.VoidFcn = dynamic_cast<Void_Action>(&test::fcn);

    Porting issues were method specific, not upcast specific.

    Sorry, my bad; (rf. Caveat(s)); and yes, cleaning up a pile of anonymous objects
    can lead to interesting side effects.

    Thanks,
    itsmike


  • Granted

     itsmike wrote:
    Caveat(s):

    Kind of ugly, and defeats the purpose of strong language typing.
    Runtime execution

    I think you are missing the point of his question. He is asking about organizing classes across multiple files where there is circular referencing. You are talking about rolling your own RTTI (which is entirely different and usually not a good idea, since C++ already has built-in RTTI that you can use in most cases other than when working across multiple executables).

    To the original poster, there's not really much to it considering you seem to understand forward declarations. Just about any time where all you need is a declaration you should just forward declare the object rather than including the entire definition (I.E.when the type in question is only needed to form pointers or references, or when the type is just used as a function parameter, etc.).

    If the declaration is complex and contains implementation details, such as additional template parameters used exclusively for SFINAE, or if you often need a group of several declarations at the same time, it's often beneficial to just make a file consisting solely of declarations of your types and templates which you include to get the declarations by themselves without pulling in their entire definitions.

    It all boils down to just only using declarations where you can and using entire definitions where you have to.

     itsmike wrote:

      b.AnotherClass = reinterpret_cast<BaseClass*>(&a); // Blind b ptr to a.

    Don't ever do that. Use a static_cast or dynamic_cast to cast from a base pointer to a child pointer. reinterpret_cast will not make the appropriate conversion required, which is especially noticeable in the case of multiple inheritance with non-empty bases. Sometimes you will be able to get away with a reinterpret_cast, however it is not standard to do so. static_cast and dynamic_cast are the standard way to get the conversion you are talking about.

    Anyway, rather than rolling your own RTTI, check out the typeid operator and the associated ::std::typeinfo type. Use those as well as static_cast and dynamic_cast if you truly need casting as you describe.

    More often than not, however, upcasting to types determined at runtime is a bad idea and is indicative of a flaw in your program's design, even if you use built-in facilities as opposed to ones you create yourself. If you need functionality like that, it usually means you failed to abstract necessary concepts to the base class's interface, or you are working with a base class pointer where you should be working with a child pointer, or you are attempting to use RTTI for resolving runtime multimethods. In the latter case, check out the visitor pattern, and if you have Boost installed, take a look at Boost.Variant.

  • John RT F

    To Rivorus and all,

    I think I found what was causing this problem.  I asked a co-worker to look at my code.

    Sorry for the inconvenience!

    Mike

  • C++ header files and forward declarations