Thread: Interdependent Classes

  1. #1
    Password:
    Join Date
    Dec 2009
    Location
    NC
    Posts
    587

    Interdependent Classes

    I have 2 classes that must have members of each other (ie A is a member of B and B a member of A). How do I do this without incomplete type errors?

    Code:
    class A
    {
        public:
        A(class B &b) : _b(b) {}
    
        void func()
        {
            _b.func();
        }
    
        private:
        class B _b;
    };
    
    class B
    {
        public:
        B(class A &a) : _a(a) {}
    
        void func()
        {
            _a.func();
        }
    
        private:
        class A _a;
    };
    If you're wondering if there's a way to restructure the program so as to avoid interdependency, I don't think it is. I'm using OOAD to simulate neural networks and my problem arises from the interdependency between dendrites/synapses and neurons.

    Thanks for your time and help.

  2. #2
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Forward declare class B and store pointers as members instead of values directly.

  3. #3
    Registered User antred's Avatar
    Join Date
    Apr 2012
    Location
    Germany
    Posts
    257
    Quote Originally Posted by manasij7479 View Post
    Forward declare class B and store pointers as members instead of values directly.
    Or references.

    Code:
    class B; // forward-declare B
    
    class A
    {
        public:
        A(B &b) : _b(b) {}
     
        void func()
        {
            _b.func();
        }
     
        private:
        B& _b;
    };
     
    class B
    {
        public:
        B(A &a) : _a(a) {}
     
        void func()
        {
            _a.func();
        }
     
        private:
        A& _a;
    };
    P.S. Your func() methods will recurse infinitely. Think about that.

  4. #4
    Password:
    Join Date
    Dec 2009
    Location
    NC
    Posts
    587
    Okay, I tried using pointers and references. Both give the same error. Switching the order of the declarations doesn't help.

    Code:
    class A
    {
        public:
        A(class B *b) : _b(b)
        {
            _b->func(); // invalid use of incomplete type 'struct B'
        }
    
        void func() {}
    
        private:
        class B *_b;
    };
    
    class B
    {
        public:
        B(class A *a) : _a(a)
        {
            _a->func();
        }
    
        void func() {}
    
        private:
        class A *_a;
    };

  5. #5
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Quote Originally Posted by User Name: View Post
    Okay, I tried using pointers and references. Both give the same error. Switching the order of the declarations doesn't help.
    You forgot the part about forward declaration.

  6. #6
    Password:
    Join Date
    Dec 2009
    Location
    NC
    Posts
    587
    I'm not sure what a forward declaration is. I think you would just put "class B;" before the declaration of A, right? If that's it, it doesn't work either. I still get the error about invalid use of an incomplete type.

    Code:
    class B;
    
    class A
    {
    ...
    };
    
    class B
    {
    ...
    };
    Is that right?

    Thanks for working with me.

  7. #7
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    I don't know of any direct solution in that case (members being used).
    So, here is an indirect one.
    Code:
    class Foo
    {
    public:virtual void func()=0;
    };
    class A
    {
        public:
        A( Foo *b) : _b(b)//You can pass an object of class B here, its own func() will get called
        {
            _b->func(); //Virtual magic happens :D
        }
    
        void func() {}
    
        private:
        Foo *_b;
    };
    
    class B : public Foo
    {
        public:
        B(A *a) : _a(a)
        {
            _a->func();
        }
    
        void func() {}
    
        private:
        A *_a;
    };
    Note that you'll almost never have to solve any problem that needs two classes to depend on each other like this. (AFAIK)

  8. #8
    Password:
    Join Date
    Dec 2009
    Location
    NC
    Posts
    587
    Ew, that's disgusting. :P Clever, though.

    Now that I think about it, I guess the class representing connections between neurons could be a subclass of the type of neurons it is supposed to connect. I'll do that (make B a subclass of A).

    Thanks for your help. It is really frustrating that there isn't a clean solution, but you were very helpful and patient and I appreciate that.

  9. #9
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Quote Originally Posted by User Name: View Post
    Now that I think about it, I guess the class representing connections between neurons could be a subclass of the type of neurons it is supposed to connect. I'll do that (make B a subclass of A).
    If B and A are different kinds of the same thing, make them both inherit from a base class denoting that type.
    (like a 'circle' and a 'triangle' will both inherit from 'shape' )

  10. #10
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    I still get the error about invalid use of an incomplete type.
    You can not use "by value" parameters for incomplete (declaration only) types; you need the definition of types for that.

    You also need the definition of types to use the components that are used to build that type.

    So...

    you must not declare any members (data members) of the incomplete type.
    you must not declare any methods (member functions) where the parameters use the incomplete type unless it is with reference or pointer.
    you must not define any methods within the class definition that uses members or methods or the incomplete type.

    Soma

    Code:
    class A;
    class B
    {
        void doSomething(A a); // Wrong!
        void doSomething(A & a)
        {
            a.doSomething(); // Wrong!
        }
        A a; // Wrong!
    };
    
    class A;
    class B
    {
        void doSomething(A * a); // Okay!
        void doSomething(A & a); // Okay!
        A * a; // Okay!
    };
    
    class A
    {
        void doSomething();
    };
    
    void B::doSomething(A & a)
    {
        a.doSomething(); // Okay!
    }

  11. #11
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    You can forward declare B for A and use a pointer, but you must move the body for the functions in A that uses B into an implementation file where you properly include B.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  12. #12
    Registered User antred's Avatar
    Join Date
    Apr 2012
    Location
    Germany
    Posts
    257
    Quote Originally Posted by phantomotap View Post
    you must not declare any methods (member functions) where the parameters use the incomplete type unless it is with reference or pointer.

    That's actually not quite the truth.

    Code:
    #include <iostream>
    
    struct Test; // forward declare Test
    
    Test func( Test t ); // <-- notice how both the return value type and the parameter are "by value"
    
    
    struct Test
    {
        int a;
        double b;
    };
    
    Test func( Test t )
    {
        Test copy = {};
    
        copy.a = t.a * 3;
        copy.b = t.b - 7.0;
    
        return copy;
    }
    
    
    int main()
    {
        const Test a = { 16, 51.5 };
    
        const Test b = func( a );
    
        std::cout << "b.a = " << b.a << ", b.b = " << b.b << std::endl;
    }

  13. #13
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    That's actually not quite the truth.
    You're right; that was too broad.

    Soma

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Conversions between base classes and derived classes
    By tharnier in forum C++ Programming
    Replies: 14
    Last Post: 03-18-2011, 10:50 AM
  2. Classes access other classes local variables
    By parad0x13 in forum C++ Programming
    Replies: 6
    Last Post: 01-14-2010, 04:36 AM
  3. Interdependent Classes
    By shadovv in forum C++ Programming
    Replies: 4
    Last Post: 10-12-2005, 03:22 PM
  4. Classes with Other Classes as Member Data
    By njd in forum C++ Programming
    Replies: 2
    Last Post: 09-27-2005, 09:30 AM
  5. Help accessing classes and derived classes
    By hobbes67 in forum C++ Programming
    Replies: 8
    Last Post: 07-14-2005, 02:46 PM