Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Circular dependency - I think..

Reply
Thread Tools

Circular dependency - I think..

 
 
ma740988
Guest
Posts: n/a
 
      07-27-2004
[....]
> 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..
 
Reply With Quote
 
 
 
 
John Carson
Guest
Posts: n/a
 
      07-27-2004
"ma740988" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed) m
> [....]
>> 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)

 
Reply With Quote
 
 
 
 
Karl Heinz Buchegger
Guest
Posts: n/a
 
      07-27-2004
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: http://www.velocityreviews.com/forums/(E-Mail Removed) Web: www.gascad.com

Fuer sehr grosse Werte von 2 gilt: 2 + 2 = 5
 
Reply With Quote
 
ma740988
Guest
Posts: n/a
 
      07-27-2004
Karl Heinz Buchegger <(E-Mail Removed)> wrote in message news:<(E-Mail Removed)>...
> 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.
 
Reply With Quote
 
ma740988
Guest
Posts: n/a
 
      07-27-2004
"John Carson" <(E-Mail Removed)> wrote in message news:<410646ef$(E-Mail Removed)>...
[...]
>
> 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.
 
Reply With Quote
 
John Carson
Guest
Posts: n/a
 
      07-28-2004
"ma740988" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed) m
> Karl Heinz Buchegger <(E-Mail Removed)> wrote in message
> news:<(E-Mail Removed)>...
>> 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)

 
Reply With Quote
 
John Carson
Guest
Posts: n/a
 
      07-28-2004
"ma740988" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)
> "John Carson" <(E-Mail Removed)> wrote in message
> news:<410646ef$(E-Mail Removed)>... [...]
>>
>> 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)
 
Reply With Quote
 
Karl Heinz Buchegger
Guest
Posts: n/a
 
      07-28-2004
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
(E-Mail Removed)
 
Reply With Quote
 
ma740988
Guest
Posts: n/a
 
      07-28-2004
Karl Heinz Buchegger <(E-Mail Removed)> wrote in message news:<(E-Mail Removed)>...
> 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.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Semi-circular definitions (plus circular references) Kiuhnm C++ 16 01-03-2005 03:49 AM
Re: Circular dependency Andrew Cowper Java 0 08-20-2003 03:30 PM
Re: Circular dependency John C. Bollinger Java 0 08-20-2003 02:22 PM
Re: Circular dependency John Hodgson Java 0 08-20-2003 01:22 PM
Re: Circular dependency Roedy Green Java 0 08-19-2003 07:49 PM



Advertisments