Circular dependency - I think..

Discussion in 'C++' started by ma740988, Jul 26, 2004.

  1. ma740988

    ma740988 Guest

    I think I'm caught up in a circular dependency problem. I'd thought
    forward declaration was the solution to my problem but I'm sadly
    mistaken (perphaps i've been in the lab toooo long today).
    The header file for FOO is composed of BAR, likewise BAR is composed
    of FOO.
    If I complile FOO.cpp, Visual Studio's first complaint (C2146 syntax
    error, missing ; before foo - the next complaint is BAR::FOO missing
    storage type) points to the instantiation of FOO in BAR's header(i.e
    the line FOO foo). Similarily if I compile BAR Visual first complaint
    (C2146 syntax error, missing ; before bar - the next complaint is
    FOO::BAR missing storage type) points to the instantiation of BAR in
    FOO's header (i.e the line BAR bar).

    The solution??

    Thanks in advance.

    The Code.


    #ifndef FOO_H
    #define FOO_H

    # include "bar.h"
    //class BAR;

    class FOO
    {
    public:
    FOO();
    ~FOO();
    int GetFbk();
    void ComputeTorquerCmd();
    private:
    int idx;
    void SetInUse();
    BAR bar;
    };

    #endif


    # include "foo.h"
    # include <iostream>

    FOO::FOO() : idx(0)
    {
    std::cout << " foo's constructor called " << std::endl;
    }

    FOO::~FOO()
    {
    std::cout << " foo destructing " << std::endl;
    }

    int FOO::GetFbk()
    {
    return ++idx; // for demo purposes
    }

    void FOO::ComputeTorquerCmd()
    {
    }

    void FOO::SetInUse()
    {
    bar.SetInUse();
    std::cout << " foo setting in use " << std::endl;
    }
    //////////////////////////////////////////

    #ifndef BAR_H
    #define BAR_H
    # include "foo.h"

    //class FOO;
    class BAR
    {
    public:
    BAR();
    ~BAR();
    void GetPosFbkFoo();
    int GetInUse() const;
    void SetInUse();

    private:
    int in_use;
    FOO foo;
    };

    #endif

    # include "bar.h"
    # include <iostream>

    BAR::BAR() : in_use(0)
    {
    std::cout << " bar's constructor " << std::endl;
    }
    BAR::~BAR() {}

    void BAR::GetPosFbkFoo()
    {
    SetInUse();
    if (in_use)
    {
    int jdx = foo.GetFbk();
    std::cout << jdx << std::endl;
    }
    else // do something else
    {
    foo.ComputeTorquerCmd();
    }
    }

    int BAR::GetInUse() const
    {
    return in_use;
    }

    void BAR::SetInUse()
    {
    in_use ^= 1;
    }


    An aside, I'm using google's newsreader which limits me from seeing a
    response (used sparingly) momentarily. The point being, my follow ups
    may be 'late'.

    On the one hand my advisor tells me a sound design limits
    'refractoring'. Later he tells me 'embrace change'. I'm confused.
     
    ma740988, Jul 26, 2004
    #1
    1. Advertising

  2. ma740988

    Sumit Rajan Guest

    "ma740988" <> wrote in message
    news:...
    > I think I'm caught up in a circular dependency problem. I'd thought
    > forward declaration was the solution to my problem but I'm sadly
    > mistaken (perphaps i've been in the lab toooo long today).
    > The header file for FOO is composed of BAR, likewise BAR is composed
    > of FOO.
    > If I complile FOO.cpp, Visual Studio's first complaint (C2146 syntax
    > error, missing ; before foo - the next complaint is BAR::FOO missing
    > storage type) points to the instantiation of FOO in BAR's header(i.e
    > the line FOO foo). Similarily if I compile BAR Visual first complaint
    > (C2146 syntax error, missing ; before bar - the next complaint is
    > FOO::BAR missing storage type) points to the instantiation of BAR in
    > FOO's header (i.e the line BAR bar).
    >
    > The solution??
    >
    > Thanks in advance.
    >
    > The Code.
    >
    >
    > #ifndef FOO_H
    > #define FOO_H
    >
    > # include "bar.h"
    > //class BAR;
    >
    > class FOO
    > {
    > public:
    > FOO();
    > ~FOO();
    > int GetFbk();
    > void ComputeTorquerCmd();
    > private:
    > int idx;
    > void SetInUse();
    > BAR bar;


    Use a pointer to BAR instead.

    See:
    http://www.parashift.com/c -faq-lite/misc-technical-issues.html#faq-38.12

    Regards,
    Sumit.
    --
    Sumit Rajan <>
     
    Sumit Rajan, Jul 26, 2004
    #2
    1. Advertising

  3. On 25 Jul 2004 17:33:01 -0700, ma740988 <>
    wrote:

    > I think I'm caught up in a circular dependency problem. I'd thought
    > forward declaration was the solution to my problem but I'm sadly
    > mistaken (perphaps i've been in the lab toooo long today).
    > The header file for FOO is composed of BAR, likewise BAR is composed
    > of FOO.
    > If I complile FOO.cpp, Visual Studio's first complaint (C2146 syntax
    > error, missing ; before foo - the next complaint is BAR::FOO missing
    > storage type) points to the instantiation of FOO in BAR's header(i.e
    > the line FOO foo). Similarily if I compile BAR Visual first complaint
    > (C2146 syntax error, missing ; before bar - the next complaint is
    > FOO::BAR missing storage type) points to the instantiation of BAR in
    > FOO's header (i.e the line BAR bar).
    >
    > The solution??
    >


    There is none. An object cannot contain itself, similarly an object cannot
    contain an object which contains itself. Use a pointer for one or both of
    FOO and BAR.

    john
     
    John Harrison, Jul 26, 2004
    #3
  4. ma740988 wrote:
    >
    > I think I'm caught up in a circular dependency problem. I'd thought
    > forward declaration was the solution to my problem but I'm sadly
    > mistaken (perphaps i've been in the lab toooo long today).
    > The header file for FOO is composed of BAR, likewise BAR is composed
    > of FOO.
    > If I complile FOO.cpp, Visual Studio's first complaint (C2146 syntax
    > error, missing ; before foo - the next complaint is BAR::FOO missing
    > storage type) points to the instantiation of FOO in BAR's header(i.e
    > the line FOO foo). Similarily if I compile BAR Visual first complaint
    > (C2146 syntax error, missing ; before bar - the next complaint is
    > FOO::BAR missing storage type) points to the instantiation of BAR in
    > FOO's header (i.e the line BAR bar).


    given

    class FOO
    {
    int i;
    BAR bar;
    }

    class BAR
    {
    int j;
    FOO foo
    };

    Now answer a quick question:
    What is the size of a FOO object. Assume sizeof(int) == 4

    Well: a FOO object consists of an int, that would
    be 4 bytes plus the size of an BAR object. That
    would be another 4 bytes plus the size of an FOO object.
    Which accounts for another 4 bytes plus the size of an BAR
    object. Another 4 bytes plus the size of a FOO object.
    Add another 4 bytes plus the size of an BAR object ....
    (ad infinitum)

    Answer: A FOO object would be of unlimited size. I'm quite
    sure that your computer doesn't have that much memory.

    --
    Karl Heinz Buchegger
     
    Karl Heinz Buchegger, Jul 26, 2004
    #4
  5. ma740988

    ma740988 Guest

    (ma740988) wrote in message news:<>...

    Thank you all. Trying to get my hands around STL and completely
    overlooked the fundamentals. One other question:
    Exception handling is an area i've - literally glanced at. I've seen
    code with a 'throw' at the constructor definition, however 'we're
    told' that exception specifiers add overhead to the executable. They
    seem to be a mixed blessing and we ought to avoid them.

    Cline's text(FAQ) seem to suggest using std::auto_ptr.

    Am I led to belive then that the preferred approach is option 1 versus
    2. See below.

    ////////////////////
    // Option 1.
    ////////////////////
    class BAR;
    class FOO
    {
    public:
    //
    private:
    std::auto_ptr<BAR*> bar;
    };

    FOO::FOO()
    {
    bar = new BAR; // allocate memory
    }


    ///////////
    class FOO;
    class BAR
    {
    public:

    private:
    std::auto_ptr<FOO*> foo;
    }

    BAR::BAR()
    {
    foo = new FOO; // allocate memory
    }


    ////////////////////
    // Option 2.
    ////////////////////
    class BAR;
    class FOO
    {
    public:
    //
    private:
    BAR* bar;
    };

    FOO::FOO() throw()
    {
    bar = new BAR; // try to find some memory. Throw an exception if
    failure
    }


    ///////////
    class FOO;
    class BAR
    {
    public:

    private:
    FOO* foo;
    }

    BAR::BAR() throw()
    {
    foo = new FOO; // to find some memory. Throw an exception if failure
    }
     
    ma740988, Jul 26, 2004
    #5
  6. ma740988

    John Carson Guest

    "ma740988" <> wrote in message
    news:
    > (ma740988) wrote in message
    > news:<>...
    >
    > Thank you all. Trying to get my hands around STL and completely
    > overlooked the fundamentals. One other question:
    > Exception handling is an area i've - literally glanced at. I've seen
    > code with a 'throw' at the constructor definition, however 'we're
    > told' that exception specifiers add overhead to the executable. They
    > seem to be a mixed blessing and we ought to avoid them.
    >
    > Cline's text(FAQ) seem to suggest using std::auto_ptr.
    >
    > Am I led to belive then that the preferred approach is option 1 versus
    > 2. See below.


    Neither of your Options will compile due to numerous syntax errors. More
    fundamentally:

    1. If you make your class members pointers (instead of objects) but still
    create new objects for those pointers to point to, then you don't escape the
    fundamental problem raised by Karl. When you create an instance of a FOO,
    memory gets allocated for a new instance of BAR, and this means that memory
    gets allocated for a new instance of FOO, and this means that memory gets
    allocated for a new instance of BAR, and this means that memory gets
    allocated for a new instance of FOO ... and so on forever. This won't stop
    your code from compiling, but your constructors will generate calls to each
    other in an infinite loop at runtime. The "use a pointer instead" advice
    only makes a difference if, for at least one of the classes, the pointer
    points to an existing instance of an object rather than a new one created by
    the constructor.

    2. Whether or not you make an exception specification is not what determines
    whether or not an exception gets thrown. At best, it affects the nature of
    that exception. If new cannot allocate memory, it will throw an exception
    regardless of the exception specification that you give your functions.
    For what it is worth, the exception specification of throw() declares that
    the function does not throw an exception, whereas you seem to believe the
    opposite. I think you need to do more than "glance" at the subject.


    --
    John Carson
    1. To reply to email address, remove donald
    2. Don't reply to email address (post here instead)
     
    John Carson, Jul 26, 2004
    #6
  7. ma740988

    ma740988 Guest

    (ma740988) wrote in message news:<>...

    Thank you all. Trying to get my hands around STL and completely
    overlooked the fundamentals. One other question:
    Exception handling is an area i've - literally glanced at. I've seen
    code with a 'throw' at the constructor definition, however 'we're
    told' that exception specifiers add overhead to the executable. They
    seem to be a mixed blessing and we ought to avoid them.

    Cline's text(FAQ) seem to suggest using std::auto_ptr.

    Am I led to belive then that the preferred approach is option 1 versus
    2. See below.

    ////////////////////
    // Option 1.
    ////////////////////
    class BAR;
    class FOO
    {
    public:
    //
    private:
    std::auto_ptr<BAR*> bar;
    };

    FOO::FOO()
    {
    bar = new BAR; // allocate memory
    }


    ///////////
    class FOO;
    class BAR
    {
    public:

    private:
    std::auto_ptr<FOO*> foo;
    }

    BAR::BAR()
    {
    foo = new FOO; // allocate memory
    }


    ////////////////////
    // Option 2.
    ////////////////////
    class BAR;
    class FOO
    {
    public:
    //
    private:
    BAR* bar;
    };

    FOO::FOO() throw()
    {
    bar = new BAR; // try to find some memory. Throw an exception if
    failure
    }


    ///////////
    class FOO;
    class BAR
    {
    public:

    private:
    FOO* foo;
    }

    BAR::BAR() throw()
    {
    foo = new FOO; // to find some memory. Throw an exception if failure
    }
     
    ma740988, Jul 26, 2004
    #7
  8. ma740988

    ma740988 Guest

    [...]
    >
    > Neither of your Options will compile due to numerous syntax errors. More
    > fundamentally:
    >
    > 1. If you make your class members pointers (instead of objects) but still
    > create new objects for those pointers to point to, then you don't escape the
    > fundamental problem raised by Karl. When you create an instance of a FOO,
    > memory gets allocated for a new instance of BAR, and this means that memory
    > gets allocated for a new instance of FOO, and this means that memory gets
    > allocated for a new instance of BAR, and this means that memory gets
    > allocated for a new instance of FOO ... and so on forever. This won't stop
    > your code from compiling, but your constructors will generate calls to each
    > other in an infinite loop at runtime. The "use a pointer instead" advice
    > only makes a difference if, for at least one of the classes, the pointer
    > points to an existing instance of an object rather than a new one created by
    > the constructor.


    I erred when I posted the code from memory but this variant works.
    // foo.h
    class BAR; // Forward declare BAR
    class FOO
    {
    public:
    FOO();
    ~FOO();
    private:
    int idx;
    BAR* bar;
    };
    #endif

    //foo.cpp
    FOO::FOO() : idx(0)
    {
    std::cout << " foo's constructor called " << std::endl;
    }

    FOO::~FOO()
    {
    std::cout << " foo destructing " << std::endl;
    }

    // bar.h
    class FOO; // Forward declare FOO
    class BAR
    {
    public:
    BAR();
    ~BAR();
    private:
    FOO* foo;
    };
    #endif

    // bar.cpp
    BAR::BAR() // BAR is the 'primary' class so construct FOO here
    {
    foo = new FOO();
    }
    BAR::~BAR()
    {
    delete foo;
    }

    Now when i try to use auto_ptr it compiles but I'm getting an
    exception when things are destructing. So now

    // bar.h
    class FOO;
    class BAR
    {
    public:
    BAR();
    ~BAR();

    private:
    int in_use;
    typedef std::auto_ptr<FOO> FooAutoPtr;
    FooAutoPtr foo;
    //FOO* foo;
    };
    // bar.cpp
    BAR::BAR() : in_use(0)
    {
    std::cout << " bar's constructor " << std::endl;
    BAR::FooAutoPtr foo( new FOO() );
    //foo = new FOO();
    }

    BAR::~BAR() { std::cout << " bar's destructor " << std::endl; }

    // only change to foo in foo.h is
    class BAR;
    class FOO
    {
    public:
    FOO();
    ~FOO();
    private:
    int idx;

    typedef std::auto_ptr<BAR> BarAutoPtr;
    BarAutoPtr bar;
    //BAR* bar;
    };

    > 2. Whether or not you make an exception specification is not what determines
    > whether or not an exception gets thrown. At best, it affects the nature of
    > that exception. If new cannot allocate memory, it will throw an exception
    > regardless of the exception specification that you give your functions.
    > For what it is worth, the exception specification of throw() declares that
    > the function does not throw an exception, whereas you seem to believe the
    > opposite. I think you need to do more than "glance" at the subject.

    I'll be headed in that direction very soon
     
    ma740988, Jul 26, 2004
    #8
  9. ma740988

    John Carson Guest

    "ma740988" <> wrote in message
    news:
    >
    > I erred when I posted the code from memory but this variant works.
    > // foo.h
    > class BAR; // Forward declare BAR
    > class FOO
    > {
    > public:
    > FOO();
    > ~FOO();
    > private:
    > int idx;
    > BAR* bar;
    > };
    > #endif
    >
    > //foo.cpp
    > FOO::FOO() : idx(0)
    > {
    > std::cout << " foo's constructor called " << std::endl;
    > }
    >
    > FOO::~FOO()
    > {
    > std::cout << " foo destructing " << std::endl;
    > }
    >
    > // bar.h
    > class FOO; // Forward declare FOO
    > class BAR
    > {
    > public:
    > BAR();
    > ~BAR();
    > private:
    > FOO* foo;
    > };
    > #endif
    >
    > // bar.cpp
    > BAR::BAR() // BAR is the 'primary' class so construct FOO here
    > {
    > foo = new FOO();
    > }
    > BAR::~BAR()
    > {
    > delete foo;
    > }
    >



    I see that you are not initialising the pointer to BAR contained in FOO.

    > Now when i try to use auto_ptr it compiles but I'm getting an
    > exception when things are destructing. So now
    >
    > // bar.h
    > class FOO;
    > class BAR
    > {
    > public:
    > BAR();
    > ~BAR();
    >
    > private:
    > int in_use;
    > typedef std::auto_ptr<FOO> FooAutoPtr;
    > FooAutoPtr foo;
    > //FOO* foo;
    > };
    > // bar.cpp
    > BAR::BAR() : in_use(0)
    > {
    > std::cout << " bar's constructor " << std::endl;
    > BAR::FooAutoPtr foo( new FOO() );
    > //foo = new FOO();
    > }


    You have not initialised BAR's member auto_pointer. Instead, you have
    declared another auto_pointer as a local variable of the constructor. It
    goes out of scope --- and hence calls delete on the memory pointed to ---
    when the constructor finishes. Usually the best way to initialise the member
    variable is using the initialiser list:

    BAR::BAR() : in_use(0), foo( new FOO )
    {
    std::cout << " bar's constructor " << std::endl;
    }

    If you want to initialise foo later than this, then you must use auto_ptr's
    reset member function.

    > BAR::~BAR() { std::cout << " bar's destructor " << std::endl; }
    >
    > // only change to foo in foo.h is
    > class BAR;
    > class FOO
    > {
    > public:
    > FOO();
    > ~FOO();
    > private:
    > int idx;
    >
    > typedef std::auto_ptr<BAR> BarAutoPtr;
    > BarAutoPtr bar;
    > //BAR* bar;
    > };


    I am not sure why you are getting an exception (I don't with your code,
    which may suggest that you haven't supplied the exact code). Neither of your
    member auto_ptrs is actually pointing to anything but this shouldn't cause
    an exception. The default constructor should set the raw pointer within
    auto_ptr to zero. Thus, when the auto_ptr destructor calls delete on that
    raw pointer, nothing should happen.

    Just by the way: I find it very confusing when no naming distinction is made
    between objects and pointers to objects --- especially when the names used
    are just lower case versions of the class names. I always make "p" the first
    letter in the name of any pointer. Thus I would use pfoo and pbar as the
    pointer names and use foo and bar as names of objects.


    --
    John Carson
    1. To reply to email address, remove donald
    2. Don't reply to email address (post here instead)
     
    John Carson, Jul 27, 2004
    #9
  10. "ma740988" <> wrote in message
    news:...

    Here's a more elegant way of solving the circular dependency
    problem which you posted. Here, actual objects are stored, not pointers,
    so I think that's a lot nicer. The trick is to use a vector, using a vector
    means you can actually store a Foo in a Bar object and a Bar in a Foo
    object after the initial objects are created. The trick also relies on the
    fact that the memory used to store elements in the vector is separate
    from the memory used to define the class. You could do the same
    with a dynamcially allocated array, but the vector<> is better toilet
    trained
    and cleans up after itself.

    I've no idea why anybody would do this, but it was a nice puzzle.
    In the real world you'll have to have proper constructors defined for
    Foo and Bar, in this example Foo and Bar are so simple they don't need
    constructors since they have no member data.

    I hope this helps you build a better mousetrap or solve the chicken and the
    egg problem.

    #include <vector>
    #include <iostream>
    #include <string>

    using namespace std;

    class Bar;
    class Foo
    {
    public:

    vector<Bar> _bars;
    void printMe(const string message ){ cout << "\n Hello from Foo in " <<
    message << "\n";}
    void printThem( const string message );
    };


    class Bar
    {
    public:

    vector<Foo> _foos;
    void printMe(const string message ){ cout << "\n Hello from Bar in " <<
    message << "\n";}
    void printThem(const string message);

    };

    void Foo::printThem(const string message)
    {
    for (int i=0; i<_bars.size(); ++i)
    _bars.printMe(message) ;
    }
    void Bar::printThem(const string message)
    {
    for (int i=0; i<_foos.size(); ++i)
    _foos.printMe(message) ;
    }
    int main(int argc, char* argv[])
    {
    Foo aFoo;
    Bar aBar;

    aFoo._bars.push_back(aBar);
    aBar._foos.push_back(aFoo);

    aFoo.printThem("Foo");
    aBar.printThem("Bar");

    return 0;
    }
    ..
     
    Dave Townsend, Jul 27, 2004
    #10
  11. ma740988

    ma740988 Guest

    [....]
    > I see that you are not initialising the pointer to BAR contained in FOO.

    I dont think I need to. If i do that I end up with the circular
    dependency problem. In fact I'd get an exception if i initialize the
    pointer to BAR contained in FOO. For simplicity.

    // bar.h
    #ifndef BAR_H
    #define BAR_H
    # include <memory>
    # include "foo.h"

    class FOO;
    class BAR
    {
    public:
    BAR();
    ~BAR();
    private:
    int in_use;
    FOO* foo;
    };

    //bar.cpp
    # include "bar.h"
    # include <iostream>

    BAR::BAR() : in_use(0), foo(new FOO())
    {
    std::cout << " bar's constructor " << std::endl;
    }

    BAR::~BAR()
    {
    std::cout << " bar's destructor " << std::endl; delete foo;
    }

    // foo.h
    #ifndef FOO_H
    #define FOO_H

    # include <memory>
    # include "bar.h"

    class BAR;
    class FOO
    {
    public:
    FOO();
    ~FOO();

    private:
    int idx;
    BAR* bar; // Initialize HOW??
    };

    #endif

    //foo.cpp
    # include<iostream>
    # include "foo.h"

    FOO::FOO() : idx(0)
    {
    std::cout << " foo's constructor called " << std::endl;
    }

    FOO::~FOO()
    {
    std::cout << " foo destructing " << std::endl;
    }

    // main_test.cpp
    int main()
    {
    BAR *ptrBar = new BAR;
    for (int Idx(0); Idx < 10; ++Idx)
    //ptrBar->GetPosFbkFoo();
    delete ptrBar;
    }


    [...]
    >
    > You have not initialised BAR's member auto_pointer. Instead, you have
    > declared another auto_pointer as a local variable of the constructor. It
    > goes out of scope --- and hence calls delete on the memory pointed to ---
    > when the constructor finishes. Usually the best way to initialise the member
    > variable is using the initialiser list:
    >
    > BAR::BAR() : in_use(0), foo( new FOO )
    > {
    > std::cout << " bar's constructor " << std::endl;
    > }

    Yes..

    > If you want to initialise foo later than this, then you must use auto_ptr's
    > reset member function.
    >

    Got it..

    [....]
    >
    > Just by the way: I find it very confusing when no naming distinction is made
    > between objects and pointers to objects --- especially when the names used
    > are just lower case versions of the class names. I always make "p" the first
    > letter in the name of any pointer. Thus I would use pfoo and pbar as the
    > pointer names and use foo and bar as names of objects.


    Point taken..
     
    ma740988, Jul 27, 2004
    #11
  12. ma740988

    John Carson Guest

    "ma740988" <> wrote in message
    news:
    > [....]
    >> I see that you are not initialising the pointer to BAR contained in
    >> FOO.

    > I dont think I need to. If i do that I end up with the circular
    > dependency problem. In fact I'd get an exception if i initialize the
    > pointer to BAR contained in FOO.


    If the pointer doesn't point to something, then what is the use in having
    it? You should omit it completely.

    The infinite memory allocation problem arises when you initialise the
    pointer by calling new to allocate more memory. If you initialise the
    pointer by making it point to an existing BAR object, then you don't have
    that problem. But, like I said, if you don't have an existing object that
    you want to point to, then you should just omit the pointer.

    One point of caution: if you make the pointer point to an existing object,
    then be careful with the use of auto_ptr. In particular:

    1. Never use auto_ptr to point to a static object or an object created on
    the stack.
    2. Never use auto_ptr to point to an object that is going to be deleted
    elsewhere in the program --- double deletion will most likely cause an
    exception. If the pre-existing object was pointed to by another auto_ptr,
    then you are in the clear since the original auto_ptr will no longer point
    to it (ownership is transferred), but that won't happen automatically if the
    pre-existing object is pointed to by a raw pointer.


    --
    John Carson
    1. To reply to email address, remove donald
    2. Don't reply to email address (post here instead)
     
    John Carson, Jul 27, 2004
    #12
  13. ma740988 wrote:
    >
    > [....]
    > > I see that you are not initialising the pointer to BAR contained in FOO.

    > I dont think I need to.


    That's a bad idea. At least you should set it to 0.
    But read on

    > If i do that I end up with the circular
    > dependency problem.


    Hmm. You shouldn't. You are dealing with pointers only, thus
    a forward declaration is sufficient.

    > In fact I'd get an exception if i initialize the
    > pointer to BAR contained in FOO.


    There is a bug in the posted main() function which deletes
    the BAR object 10 times. COuld be an explanation for your problem.

    Anyway:
    Note how I used the 'this' pointer in the intitializer list of BAR
    to pass a pointer to the created FOO object.

    #include <iostream>

    class BAR;
    class FOO
    {
    public:
    FOO( BAR* bar_ );
    ~FOO();

    private:
    int idx;
    BAR* bar;
    };

    FOO::FOO( BAR* bar_ ) : idx(0), bar( bar_ )
    {
    std::cout << " foo's constructor called " << std::endl;
    }

    FOO::~FOO()
    {
    std::cout << " foo destructing " << std::endl;
    }

    class BAR
    {
    public:
    BAR();
    ~BAR();
    private:
    int in_use;
    FOO* foo;
    };

    BAR::BAR() : in_use(0), foo(new FOO( this ))
    {
    std::cout << " bar's constructor " << std::endl;
    }

    BAR::~BAR()
    {
    std::cout << " bar's destructor " << std::endl; delete foo;
    }

    int main()
    {
    BAR *ptrBar = new BAR;
    for (int Idx(0); Idx < 10; ++Idx)
    //ptrBar->GetPosFbkFoo();
    ;

    delete ptrBar;
    }


    --
    Karl Heinz Buchegger, GASCAD GmbH
    Teichstrasse 2
    A-4595 Waldneukirchen
    Tel ++43/7258/7545-0 Fax ++43/7258/7545-99
    email: Web: www.gascad.com

    Fuer sehr grosse Werte von 2 gilt: 2 + 2 = 5
     
    Karl Heinz Buchegger, Jul 27, 2004
    #13
  14. ma740988

    ma740988 Guest

    Karl Heinz Buchegger <> wrote in message news:<>...
    > ma740988 wrote:
    > >
    > > [....]
    > > > I see that you are not initialising the pointer to BAR contained in FOO.

    > > I dont think I need to.

    >
    > That's a bad idea. At least you should set it to 0.

    I agree wholeheartedly, but lets capitalize on this in a minute.

    [...]
    >
    > Anyway:
    > Note how I used the 'this' pointer in the intitializer list of BAR
    > to pass a pointer to the created FOO object.

    This is embarassing. I understand the concept, read the books but yet
    I completely missed yet again an important fundamental. Practise - I
    suspect - makes perfect.
    >
    > #include <iostream>
    >
    > class BAR;
    > class FOO
    > {
    > public:
    > FOO( BAR* bar_ );
    > ~FOO();
    >
    > private:
    > int idx;


    Lets try to use std::auto_ptr. To do this we'll make a few changes.
    // BAR* bar; // commented out
    typedef std::auto_ptr<BAR> BarAutoPtr;
    BarAutoPtr bar;
    > };
    >
    > FOO::FOO( BAR* bar_ ) : idx(0), bar( bar_ )
    > {
    > std::cout << " foo's constructor called " << std::endl;
    > }
    >
    > FOO::~FOO()
    > {
    > std::cout << " foo destructing " << std::endl;
    > }
    >
    > class BAR
    > {
    > public:
    > BAR();
    > ~BAR();
    > private:
    > int in_use;

    More changes.
    // FOO* foo;
    typedef std::auto_ptr<FOO> FooAutoPtr;
    FooAutoPtr foo;
    > };
    >
    > BAR::BAR() : in_use(0), foo(new FOO( this ))
    > {
    > std::cout << " bar's constructor " << std::endl;
    > }
    >
    > BAR::~BAR()
    > {
    > std::cout << " bar's destructor " << std::endl; delete foo;
    > }
    >
    > int main()
    > {
    > BAR *ptrBar = new BAR;

    [...]
    > delete ptrBar;
    > }

    Recall that's its a bad idea to NOT initialize the pointer to BAR
    contained in FOO. This doesn't work for auto_ptr. Why the
    discrepancy & how do you initialize bar??

    Thanks for your time.
     
    ma740988, Jul 27, 2004
    #14
  15. ma740988

    ma740988 Guest

    "John Carson" <> wrote in message news:<410646ef$>...
    [...]
    >
    > The infinite memory allocation problem arises when you initialise the
    > pointer by calling new to allocate more memory. If you initialise the
    > pointer by making it point to an existing BAR object, then you don't have
    > that problem. But, like I said, if you don't have an existing object that
    > you want to point to, then you should just omit the pointer.


    an existing BAR object. Tried that. See my response to Karl.

    > One point of caution: if you make the pointer point to an existing object,
    > then be careful with the use of auto_ptr. In particular:
    >
    > 1. Never use auto_ptr to point to a static object or an object created on
    > the stack.

    I think the solution to my response to Karl lies here, however, bar
    was not created on the stack and when handed to foo (via this) I still
    end up with circular dependency so item 1 doens't apply?

    > 2. Never use auto_ptr to point to an object that is going to be deleted
    > elsewhere in the program --- double deletion will most likely cause an
    > exception. If the pre-existing object was pointed to by another auto_ptr,
    > then you are in the clear since the original auto_ptr will no longer point
    > to it (ownership is transferred), but that won't happen automatically if the
    > pre-existing object is pointed to by a raw pointer.


    item 2. i dont think I violated either.
     
    ma740988, Jul 27, 2004
    #15
  16. ma740988

    John Carson Guest

    "ma740988" <> wrote in message
    news:
    > Karl Heinz Buchegger <> wrote in message
    > news:<>...
    >> ma740988 wrote:
    >>>
    >>> [....]
    >>>> I see that you are not initialising the pointer to BAR contained
    >>>> in FOO.
    >>> I dont think I need to.

    >>
    >> That's a bad idea. At least you should set it to 0.

    > I agree wholeheartedly, but lets capitalize on this in a minute.
    >
    > [...]
    >>
    >> Anyway:
    >> Note how I used the 'this' pointer in the intitializer list of BAR
    >> to pass a pointer to the created FOO object.

    > This is embarassing. I understand the concept, read the books but yet
    > I completely missed yet again an important fundamental. Practise - I
    > suspect - makes perfect.
    >>
    >> #include <iostream>
    >>
    >> class BAR;
    >> class FOO
    >> {
    >> public:
    >> FOO( BAR* bar_ );
    >> ~FOO();
    >>
    >> private:
    >> int idx;

    >
    > Lets try to use std::auto_ptr. To do this we'll make a few changes.
    > // BAR* bar; // commented out
    > typedef std::auto_ptr<BAR> BarAutoPtr;
    > BarAutoPtr bar;
    >> };
    >>
    >> FOO::FOO( BAR* bar_ ) : idx(0), bar( bar_ )
    >> {
    >> std::cout << " foo's constructor called " << std::endl;
    >> }
    >>
    >> FOO::~FOO()
    >> {
    >> std::cout << " foo destructing " << std::endl;
    >> }
    >>
    >> class BAR
    >> {
    >> public:
    >> BAR();
    >> ~BAR();
    >> private:
    >> int in_use;

    > More changes.
    > // FOO* foo;
    > typedef std::auto_ptr<FOO> FooAutoPtr;
    > FooAutoPtr foo;
    >> };
    >>
    >> BAR::BAR() : in_use(0), foo(new FOO( this ))
    >> {
    >> std::cout << " bar's constructor " << std::endl;
    >> }
    >>
    >> BAR::~BAR()
    >> {
    >> std::cout << " bar's destructor " << std::endl; delete foo;
    >> }
    >>
    >> int main()
    >> {
    >> BAR *ptrBar = new BAR;

    > [...]
    >> delete ptrBar;
    >> }

    > Recall that's its a bad idea to NOT initialize the pointer to BAR
    > contained in FOO. This doesn't work for auto_ptr. Why the
    > discrepancy & how do you initialize bar??
    >
    > Thanks for your time.



    One obvious problem: BAR now has an auto_ptr to FOO, so BAR's destructor
    should no longer delete the FOO object.

    A general observation: you are making things hard for yourself by creating
    links between classes without having a clear motive for doing so. The logic
    of a problem can often guide you through the required syntax. If you are
    just playing with syntax without having a problem to solve, then it is much
    harder to figure out what to do.

    In Karl's code, BAR stores a pointer to a newly created FOO and the FOO thus
    created stores a pointer to the BAR that created it. In real world code, the
    reason for doing this might be so that the FOO object can call member
    functions of its BAR creator.

    When you use an auto_ptr in FOO to point to BAR, you are giving the FOO
    object the job of deleting its BAR creator. BAR is already deleted in main()
    by delete ptrBar so you have a double deletion problem.

    What happens is the following. delete ptrBar causes BAR's destructor to be
    called. The first stage in the destruction of an object is the destruction
    of its member objects. Thus the destructor of BAR's smart_ptr to FOO is
    called. This deletes the FOO object, generating a call to the destructor of
    FOO, leading to a call to the destructor of FOO's smart_ptr to BAR. This
    leads to a call to the destructor of BAR and we start again, repeating
    forever.

    Another way of looking at this is that, just as you get infinite recursion
    when two classes create instances of each other in their constructors, so
    too you get infinite recursion when two classes destroy instances of each
    other in their destructors. This means that two classes should *not* have
    auto_ptrs to each other.

    FOO should have a regular pointer to BAR and you should rely on the delete
    ptrBar in main to delete the BAR object.


    --
    John Carson
    1. To reply to email address, remove donald
    2. Don't reply to email address (post here instead)
     
    John Carson, Jul 28, 2004
    #16
  17. ma740988

    John Carson Guest

    "ma740988" <> wrote in message
    news:
    > "John Carson" <> wrote in message
    > news:<410646ef$>... [...]
    >>
    >> 2. Never use auto_ptr to point to an object that is going to be
    >> deleted elsewhere in the program --- double deletion will most
    >> likely cause an exception. If the pre-existing object was pointed to
    >> by another auto_ptr, then you are in the clear since the original
    >> auto_ptr will no longer point to it (ownership is transferred), but
    >> that won't happen automatically if the pre-existing object is
    >> pointed to by a raw pointer.

    >
    > item 2. i dont think I violated either.


    Actually you did. See my comments on your reply to Karl.

    --
    John Carson
    1. To reply to email address, remove donald
    2. Don't reply to email address (post here instead)
     
    John Carson, Jul 28, 2004
    #17
  18. John Carson wrote:
    >

    [snip]
    >
    > A general observation: you are making things hard for yourself by creating
    > links between classes without having a clear motive for doing so. The logic
    > of a problem can often guide you through the required syntax. If you are
    > just playing with syntax without having a problem to solve, then it is much
    > harder to figure out what to do.
    >
    > In Karl's code, BAR stores a pointer to a newly created FOO and the FOO thus
    > created stores a pointer to the BAR that created it. In real world code, the
    > reason for doing this might be so that the FOO object can call member
    > functions of its BAR creator.
    >
    > When you use an auto_ptr in FOO to point to BAR, you are giving the FOO
    > object the job of deleting its BAR creator. BAR is already deleted in main()
    > by delete ptrBar so you have a double deletion problem.

    [snip]

    To ma740988:

    At this level in your study you should also start to differentiate
    pointers into 2 categories:
    * owning pointers
    * non owning pointers

    This is a purely programmers concept and isn't enforced by anything
    in the language per se.

    A owning pointer is a pointer, who is completly responsible for the object
    it points to. It eventually has to delete the object.
    A non owning pointer on the other hand is just that: A pointer to an object.
    No more no less. A non owning pointer doesn't have the right to delete the
    object it points to.

    There can be many non owning pointers for the same object. But there should
    be only one owning pointer. Nothing is enforcing this and in fact if one
    is carefull it is quite possible to have more then one owning pointer, but
    practice shows that eventually it will lead to troubles in the form you
    have discovered.

    Oh. BTW. An auto_ptr is due to its semantics always an owning pointer.

    So whenever you introduce a pointer, ask yourself: owning or not owning?
    If the answer is not owning, then stay away of delete's through this pointer.

    Back to my example.
    Should the pointer in FOO be an owning pointer? I don't think so. main() deals
    with BAR only and it is the BAR object that creates the FOO object, thus BAR
    'has the lead' and should manage the FOO object. The FOO object can hold a pointer
    to BAR, but it is *not* FOO's reponsibility to manage the BAR object. Thus
    having an auto_ptr in FOO isn't a good idea.

    Years ago, when I started with C++, I pretty much made the same mistake. I
    created a small graphical editor. The structural hierarchy was: objects
    consist of faces, faces of points. I found it a good idea, that when I
    delete a pointer that pointer should delete itself from the face and when
    the face becomes empty the point deletes the face. Thus the point got
    backpointers into all the faces it was a member of. To make a long story short:
    It ended in a mess. I finally made it work, but don't ask ....
    The lesson i learned was: A circular owning semantic isn't a good idea.
    Better create a clear hierarchy: objects manage faces, faces manage points
    and stick with it.

    --
    Karl Heinz Buchegger
     
    Karl Heinz Buchegger, Jul 28, 2004
    #18
  19. ma740988

    ma740988 Guest

    Karl Heinz Buchegger <> wrote in message news:<>...
    > John Carson wrote:

    [...]
    >
    > To ma740988:
    >
    > At this level in your study you should also start to differentiate
    > pointers into 2 categories:
    > * owning pointers
    > * non owning pointers


    Good stuff

    > This is a purely programmers concept and isn't enforced by anything
    > in the language per se.
    >
    > A owning pointer is a pointer, who is completly responsible for the object
    > it points to. It eventually has to delete the object.
    > A non owning pointer on the other hand is just that: A pointer to an object.
    > No more no less. A non owning pointer doesn't have the right to delete the
    > object it points to.
    >
    > There can be many non owning pointers for the same object. But there should
    > be only one owning pointer. Nothing is enforcing this and in fact if one
    > is carefull it is quite possible to have more then one owning pointer, but
    > practice shows that eventually it will lead to troubles in the form you
    > have discovered.
    >
    > Oh. BTW. An auto_ptr is due to its semantics always an owning pointer.
    >
    > So whenever you introduce a pointer, ask yourself: owning or not owning?
    > If the answer is not owning, then stay away of delete's through this pointer.
    >
    > Back to my example.
    > Should the pointer in FOO be an owning pointer? I don't think so. main() deals
    > with BAR only and it is the BAR object that creates the FOO object, thus BAR
    > 'has the lead' and should manage the FOO object. The FOO object can hold a pointer
    > to BAR, but it is *not* FOO's reponsibility to manage the BAR object. Thus
    > having an auto_ptr in FOO isn't a good idea.
    >
    > Years ago, when I started with C++, I pretty much made the same mistake. I
    > created a small graphical editor. The structural hierarchy was: objects
    > consist of faces, faces of points. I found it a good idea, that when I
    > delete a pointer that pointer should delete itself from the face and when
    > the face becomes empty the point deletes the face. Thus the point got
    > backpointers into all the faces it was a member of. To make a long story short:
    > It ended in a mess. I finally made it work, but don't ask ....
    > The lesson i learned was: A circular owning semantic isn't a good idea.
    > Better create a clear hierarchy: objects manage faces, faces manage points
    > and stick with it.


    I now see the light. Thank you all.
     
    ma740988, Jul 28, 2004
    #19
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Roedy Green

    Re: Circular dependency

    Roedy Green, Aug 19, 2003, in forum: Java
    Replies:
    0
    Views:
    402
    Roedy Green
    Aug 19, 2003
  2. John Hodgson

    Re: Circular dependency

    John Hodgson, Aug 20, 2003, in forum: Java
    Replies:
    0
    Views:
    425
    John Hodgson
    Aug 20, 2003
  3. Andrew Cowper

    Re: Circular dependency

    Andrew Cowper, Aug 20, 2003, in forum: Java
    Replies:
    0
    Views:
    409
    Andrew Cowper
    Aug 20, 2003
  4. John C. Bollinger

    Re: Circular dependency

    John C. Bollinger, Aug 20, 2003, in forum: Java
    Replies:
    0
    Views:
    413
    John C. Bollinger
    Aug 20, 2003
  5. Kiuhnm
    Replies:
    16
    Views:
    764
    Jonathan Mcdougall
    Jan 3, 2005
Loading...

Share This Page