Passing *this in constructor

T

tech

Hi, thanks for help in previous post. I've made the class objects i
need data members so
i now initialise in constructor. However some of my subobjects need to
have a reference to their
owner object.

Can i do the following then ie pass *this in the constructor of A, i
was concerned maybe the
A object is not fully constructed. If this is not way to do how to do?

/.h file
class A
{
public:
A();
~A();
private:

obj1 myobj1;



};


/.cpp file

A::A()
: myobj1(*this) <- CHANGE MADE HERE
{



}

A::~A()
{


}


Obviously constructor of obj1 takes reference to A

class obj1
{
private:
obj1& m_obj;
};

obj1(A& a):m_obj(a)
{
}
 
J

James Kanze

I think you meant
A& m_obj;
The answer is "Yes, you can". Be careful, though, not to call
any member functions or access any data members of 'a' in the
'obj1' constructor because 'a' hasn't been fully constructed
yet.

The answer is "yes, you can" in this simple case. If the object
expects a reference to a base class, however, you have undefined
behavior (and I've encountered cases where it doesn't work),
since conversion to base is also undefined behavior. Why this
is so, I don't know, since the compiler must be able to do the
conversion in order to call the constructor of the base classes,
or to call functions in the base classes from within the
constructor, but I've had it actuallly fail (admittedly only in
cases of virtual inheritance).
 
F

Frank Birbacher

Hi!

James said:
The answer is "yes, you can" in this simple case. If the object
expects a reference to a base class, however, you have undefined
behavior (and I've encountered cases where it doesn't work),
since conversion to base is also undefined behavior.

Wait a second. What's that? Has the following undefined behaviour?

struct Foo {
private:
//..lots of member variables needing initialisation
string name;
Bar whatever;
protected:
void run();
};

struct Runner : Foo
{
Runner();
~Runner();
boost::thread myThread;
};

Runner::Runner()
: Foo()
, myThread(bind(&Foo::run, this))
{}
Runner::~Runner()
{
myThread.interrupt();
myThread.join();
}

Maybe this doesn't actually force the conversion of "this" to "Foo*",
but it's an example of what I currently need in some code. I just want
to know some rules about this. So when does it become dangerous?

Frank
 
F

Frank Birbacher

Hi!

Victor said:
Note, that the Bar subobject is initialised before the construction of
the 'Foo' subobject begins.

Ok. That's fine.

However James seemed to say you cannot cast "*this" into a base class
reference:

struct Base {};
struct HoldBase {
HoldBase(Base&);
};

struct Derived : Base {
Derived()
: Base(), hold(*this)
{}
HoldBase hold;
};

This transforms "*this" into a base class reference during
initialization. I interpreted James posting this way and concluded the
above must be invalid/undefined behaviour. And this seems rather odd,
don't you think?

Frank
 
J

James Kanze

James Kanze schrieb:
Wait a second. What's that? Has the following undefined behaviour?
struct Foo {
private:
//..lots of member variables needing initialisation
string name;
Bar whatever;
protected:
void run();
};
struct Runner : Foo
{
Runner();
~Runner();
boost::thread myThread;
};
Runner::Runner()
: Foo()
, myThread(bind(&Foo::run, this))
{}
Runner::~Runner()
{
myThread.interrupt();
myThread.join();
}

That's boost::bind, right. A template. So the function will
adopt to the type of this, and no conversion will be necessary.
This is a problem with a lot of home built thread classes,
which, even worse, will offen convert the derived this to a
void* (legal, but...), only to convert the void* to Base* (and
not Derived*) in the start up routine.
Maybe this doesn't actually force the conversion of "this" to
"Foo*", but it's an example of what I currently need in some
code. I just want to know some rules about this. So when does
it become dangerous?

The problem here is elsewhere: you're starting a thread in the
constructor of the thread object, and that's a recepe for
disaster, and something you rigorously want to avoid. (What
happens if someone comes along and derives from Runner?) In
this case, I don't think it's really necessary in your design,
either: you could use containment, and not derivation here, for
example, although I don't like the fact that you then depend on
the order of initialization---it works, but it is fragile,
because the dependency isn't obvious at the place where the
order is determined; the class definition. And even better
solution might be for Runner to maintain an auto_ptr to Foo.
 
J

James Kanze

Victor Bazarov schrieb:
However James seemed to say you cannot cast "*this" into a
base class reference:
struct Base {};
struct HoldBase {
HoldBase(Base&);
};
struct Derived : Base {
Derived()
: Base(), hold(*this)
{}
HoldBase hold;
};
This transforms "*this" into a base class reference during
initialization. I interpreted James posting this way and
concluded the above must be invalid/undefined behaviour. And
this seems rather odd, don't you think?

It is undefined behavior, and I think it rather odd, too. The
reason I know it's undefined behavior, however, is that I ran
into an actual case where it didn't work, went to write up a
compiler bug report, and when trying to find where the standard
said it had to work, found just the opposite.

One way around it is to use a wrapper class which returns the
pointer---you can't convert the pointer, but you can call member
funtions on fully constructed sub-objects, so something like:

template< typename Base >
struct BaseWrapper
{
Base object ;
Base* ptr() ;
Base& ref() ;
} ;

struct Derived : BaseWrapper< Base >
: hold( ref() )
{
} ;

Although useful in some specific cases (I use it when deriving
from both a specific streambuf and istream or ostream), it is
awkward and overly complex in the general case. And if the
compiler is required to get its pointer conversions correct in
this case, why can't it do so in the other case?
 
F

Frank Birbacher

Hi!

James said:
It is undefined behavior, and I think it rather odd, too. The
reason I know it's undefined behavior, however, is that I ran
into an actual case where it didn't work, went to write up a
compiler bug report, and when trying to find where the standard
said it had to work, found just the opposite.

Eeew. That's bad!
One way around it is to use a wrapper class which returns the
pointer---you can't convert the pointer, but you can call member
funtions on fully constructed sub-objects, so something like:

Or like:

struct Foo
{
//...here reguar definition
protected:
Foo* ptr() { return this; }
Foo* ref() { return *this; }
};

struct Dev : Foo
{
Dev();
Foo& useless;
};

Dev::Dev()
: Foo(), useless(Foo::ref())
{}

Would that be ok?

I call this a serious defect in the standard. Like you said, you didn't
expect undefined behaviour here anywhere. The conversion is simple to do
because it is needed for calling base class functions anyway. The above
ptr() and ref() methods show an easy implementation and proof this can
be done easily. Not specifying behaviour for conversions here is just a
dangerous pitfall!

Any chance this could be fixed in C++0x?

Frank
 
F

Frank Birbacher

Hi!

James said:
That's boost::bind, right. A template. So the function will
adopt to the type of this, and no conversion will be necessary.

Actually a conversion is eventually necessary: in calling the base class
run() method. If "target" was a pointer to the derived class, would
target ->* (&Base::run)();
include an undefined conversion? This conversion would take place in a
different thread but because of this might as well be done still during
initialization of Derived.
This is a problem with a lot of home built thread classes,
which, even worse, will offen convert the derived this to a
void* (legal, but...), only to convert the void* to Base* (and
not Derived*) in the start up routine.

Woah, bad bad bad. No more torture using void pointers please. ;)
The problem here is elsewhere:

Right, design could be better. I'm still working on it. It's a local
definition for instantiating a static variable which is used as a
singleton. It has had more problems before I refactored it to use boost
thread.

Frank
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,008
Latest member
Rahul737

Latest Threads

Top