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. 18 1665
"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>
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
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 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
}
"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) 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
}
[...] 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" <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)
"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;
}
..
[....] 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" <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)
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
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.
"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.
"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)
"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)
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
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. This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
by: Evelyne |
last post by:
On a server W2K, IIS5, I have many messages from ASP :
cirdular dependency between types/modules. Which can be
the origin of the problem?
Example :
Event Type: Warning
Event Source: Active...
|
by: Henry Miller |
last post by:
I have the following code (much simplified for this post). Note that
SessionKey uses DataAccess, and DataAccess requires SessionKey in it's
constructor.
Public Class SessionKey
Public...
|
by: ernesto basc?n pantoja |
last post by:
Hi everybody:
I'm implementing a general C++ framework and I have a basic question
about circular dependencies:
I am creating a base class Object, my Object class has a method
defined as:...
|
by: ro86 |
last post by:
Hello everyone!
I am a newbie to C++ (~1 Week experience) and I have a few months of
experience with object-oriented languages (Objective-C). I am
currently working just for fun on a particle...
|
by: Henke |
last post by:
I have this scenario.
public class A
{
public int numbers;
public class A()
{
}
|
by: Rimma Meital via .NET 247 |
last post by:
(Type your message here)
--------------------------------
From: Rimma Meital
I have 2 DLLs (Class Libraries). Both defines single-ton objects - logger object (writes to logger) and...
|
by: Jeff Connelly |
last post by:
We're getting this error and don't know where to find the problem. I assume
this is usually issued in the typical case where project A has a reference
to project B, and project B has a reference...
|
by: Rob Clark |
last post by:
Hi,
I have an issue which is the ordinary and always recurring circular
dependency - but I do not know how to resolve it :-( The usual forward
declaration does not help...
I have a client...
|
by: barias |
last post by:
Although circular dependencies are something developers should normally
avoid, unfortunately they are very easy to create accidentally between
classes in a VS project (i.e. circular compile-time...
|
by: donnyma |
last post by:
I have a problem that looks like it has not been discussed before in
these groups.
I have a simple SQLAgent job that runs sp_who (could be anything, but
let's just say sp_who for this example). ...
|
by: BarryA |
last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
|
by: nemocccc |
last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
|
by: Sonnysonu |
last post by:
This is the data of csv file
1 2 3
1 2 3
1 2 3
1 2 3
2 3
2 3
3
the lengths should be different i have to store the data by column-wise with in the specific length.
suppose the i have to...
|
by: Hystou |
last post by:
There are some requirements for setting up RAID:
1. The motherboard and BIOS support RAID configuration.
2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
|
by: Hystou |
last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
|
by: jinu1996 |
last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
|
by: tracyyun |
last post by:
Dear forum friends,
With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
|
by: agi2029 |
last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
|
by: conductexam |
last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...
| |