471,089 Members | 1,294 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 471,089 software developers and data experts.

Circular dependency - I think..

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.
Jul 22 '05 #1
18 1578

"ma740988" <ma******@pegasus.cc.ucf.edu> wrote in message
news:a5*************************@posting.google.co m...
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-lit...html#faq-38.12

Regards,
Sumit.
--
Sumit Rajan <su********@myrealbox.com>
Jul 22 '05 #2
On 25 Jul 2004 17:33:01 -0700, ma740988 <ma******@pegasus.cc.ucf.edu>
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
Jul 22 '05 #3
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
kb******@gascad.at
Jul 22 '05 #4
ma******@pegasus.cc.ucf.edu (ma740988) wrote in message news:<a5*************************@posting.google.c om>...

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
}
Jul 22 '05 #5
"ma740988" <ma******@pegasus.cc.ucf.edu> wrote in message
news:a5************************@posting.google.com
ma******@pegasus.cc.ucf.edu (ma740988) wrote in message
news:<a5*************************@posting.google.c om>...

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)
Jul 22 '05 #6
ma******@pegasus.cc.ucf.edu (ma740988) wrote in message news:<a5*************************@posting.google.c om>...

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
}
Jul 22 '05 #7
[...]

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
Jul 22 '05 #8
"ma740988" <ma******@pegasus.cc.ucf.edu> wrote in message
news:a5*************************@posting.google.co m

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)

Jul 22 '05 #9

"ma740988" <ma******@pegasus.cc.ucf.edu> wrote in message
news:a5*************************@posting.google.co m...

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[i].printMe(message) ;
}
void Bar::printThem(const string message)
{
for (int i=0; i<_foos.size(); ++i)
_foos[i].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;
}
..
Jul 22 '05 #10
[....]
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..
Jul 22 '05 #11
"ma740988" <ma******@pegasus.cc.ucf.edu> wrote in message
news:a5*************************@posting.google.co 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)

Jul 22 '05 #12
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: kb******@gascad.at Web: www.gascad.com

Fuer sehr grosse Werte von 2 gilt: 2 + 2 = 5
Jul 22 '05 #13
Karl Heinz Buchegger <kb******@gascad.at> wrote in message news:<41***************@gascad.at>...
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.
Jul 22 '05 #14
"John Carson" <do***********@datafast.net.au> wrote in message news:<41********@usenet.per.paradox.net.au>...
[...]

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.
Jul 22 '05 #15
"ma740988" <ma******@pegasus.cc.ucf.edu> wrote in message
news:a5*************************@posting.google.co m
Karl Heinz Buchegger <kb******@gascad.at> wrote in message
news:<41***************@gascad.at>...
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)

Jul 22 '05 #16
"ma740988" <ma******@pegasus.cc.ucf.edu> wrote in message
news:a5************************@posting.google.com
"John Carson" <do***********@datafast.net.au> wrote in message
news:<41********@usenet.per.paradox.net.au>... [...]

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)
Jul 22 '05 #17
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
kb******@gascad.at
Jul 22 '05 #18
Karl Heinz Buchegger <kb******@gascad.at> wrote in message news:<41**************@gascad.at>...
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.
Jul 22 '05 #19

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

4 posts views Thread by Evelyne | last post: by
1 post views Thread by Henry Miller | last post: by
2 posts views Thread by ernesto basc?n pantoja | last post: by
4 posts views Thread by Henke | last post: by
2 posts views Thread by Rimma Meital via .NET 247 | last post: by
8 posts views Thread by Jeff Connelly | last post: by
3 posts views Thread by Rob Clark | last post: by
7 posts views Thread by barias | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.