Assign Reference to another Referance

C

Christopher

How can I assign a reference to another reference, making them both
refer to the same object?
The object does not have an assignment operator. Nor do I want to
assign right hand to left hand by value, I want two references to the
same object.




#ifndef RENDERABLE_H
#define RENDERABLE_H

// EngineX Includes
#include "Graphics\3D\Transform.h"
#include "Graphics\Effects\EffectManager.h"

// DirectX Includes
#include <d3d10.h>
#include <d3dx10.h>

// Boost Includes
#include <boost/shared_ptr.hpp>

// Standard Includes
#include <string>

//----------------------------------------------------------------------------------------------------------------------
class Renderable : public Transform
{
public:

// SNIP

Renderable & operator = (const Renderable & rhs);

// SNIP

protected:

RenderType m_renderType; // Determines
how to queue renderables (see Renderqueue.h)

ID3D10Device & m_device; // D3D device
EffectManager & m_effectManager; // Contains
and creates effects and techniques

std::string m_effectName; // Name of
the effect to use when rendering
std::string m_techniqueName; // Name of
the technique to use when rendering
std::auto_ptr<Material> m_material; // Material
hold all effect variable values to use when rendering



private:

};

#endif RENDERABLE_H

//----------------------------------------------------------------------------------------------------------------------
Renderable & Renderable::eek:perator = (const Renderable & rhs)
{
if( this == &rhs )
{
return *this;
}

this->Transform::eek:perator = (rhs);

// Error Here
m_device = rhs.m_device;
m_effectManager = rhs.m_effectManager;
m_renderType = rhs.m_renderType;

return *this;
}
 
C

cpisz

How can I assign a reference to another reference, making them both
refer to the same  object?
The object does not have an assignment operator. Nor do I want to
assign right hand to left hand by value, I want two references to the
same object.

#ifndef RENDERABLE_H
#define RENDERABLE_H

// EngineX Includes
#include "Graphics\3D\Transform.h"
#include "Graphics\Effects\EffectManager.h"

// DirectX Includes
#include <d3d10.h>
#include <d3dx10.h>

// Boost Includes
#include <boost/shared_ptr.hpp>

// Standard Includes
#include <string>

//-------------------------------------------------------------------------­---------------------------------------------
class Renderable : public Transform
{
public:

   // SNIP

   Renderable & operator = (const Renderable & rhs);

    // SNIP

   protected:

   RenderType                      m_renderType;         // Determines
how to queue renderables (see Renderqueue.h)

   ID3D10Device &                  m_device;             // D3D device
   EffectManager &                 m_effectManager;      // Contains
and creates effects and techniques

   std::string                     m_effectName;         // Name of
the effect to use when rendering
   std::string                     m_techniqueName;      // Name of
the technique to use when rendering
   std::auto_ptr<Material>         m_material;           // Material
hold all effect variable values to use when rendering

private:

};

#endif  RENDERABLE_H

//-------------------------------------------------------------------------­---------------------------------------------
Renderable & Renderable::eek:perator = (const Renderable & rhs)
{
   if( this == &rhs )
   {
      return *this;
   }

   this->Transform::eek:perator = (rhs);

   // Error Here
   m_device        = rhs.m_device;
   m_effectManager = rhs.m_effectManager;
   m_renderType    = rhs.m_renderType;

   return *this;



}- Hide quoted text -

- Show quoted text -

Well, the FAQ says you can't. I can skip it, since both objects should
have been constructed with the same members in question. However, I
would like to at least perform a check that both refer to the same
object and throw an exception if they don't. device and effectManager
really should be singletons, but I opted to not use them and just pass
a reference around instead. Singletons have caused more problems than
they are worth in the past, with release order in program exit.
 
C

cpisz

exit.

That's why singletons are often created dynamically and not
destroyed before program exit.

Paavo

I've never in all my reading seen a singleton pattern that did not
involve a global or static pointer, or reference, and thus involve
problems of dependency at program exit time when these are released.
Could you share this pattern that side steps the problem?
 
C

cpisz

Paavo Helde <[email protected]> kirjutas:












Just a clarificition - this release is a non-op as pointer does not have
any destructor, meaning that the pointer retains its value regardless of
whether the runtime considers the statics in this compilation unit
released or not. So the singleton effectively remains operative also
later.




- Show quoted text -- Hide quoted text -

- Show quoted text -


That does not circumvent the problem at all. Suppose you have a static
or global instance of a class that calls Instance() in its destructor.
Undefined behavior results at program exit as the order of destruction
is not defined. The class may or may not work with a valid instance.

Easy to get around by checking dependancies in a small project. In a
large project on which many people will be working simotaneously
(almost any job), not worth the hassle.
 
C

cpisz

That does not circumvent the problem at all. Suppose you have a static
or global instance of a class that calls Instance() in its destructor.
Undefined behavior results at program exit as the order of destruction
is not defined. The class may or may not work with a valid instance.

Easy to get around by checking dependancies in a small project. In a
large project on which many people will be working simotaneously
(almost any job), not worth the hassle.- Hide quoted text -

- Show quoted text -

I'll add that calling GetInstance is hardly ever done directly when
this problem arises....
Lot's of debugging fun that can be easily avoided.
 
J

James Kanze

How can I assign a reference to another reference, making them
both refer to the same object?

You can't reseat the reference, to make it refer to a different
object, once it has been initialized. If you need to do that,
use pointers.

You can initialize a reference with another reference.
 
J

James Kanze

I'm not too sure what you mean by "released". C++ doesn't have
a concept of "release"---do you mean "destructed", or "deleted".
(One could argue that the pointer is destructed, but that this
is a no-op. In no way is anything ever deleted, however.)
That does not circumvent the problem at all. Suppose you have
a static or global instance of a class that calls Instance()
in its destructor. Undefined behavior results at program exit
as the order of destruction is not defined. The class may or
may not work with a valid instance.

No it doesn't. I'd suggest that you check the code again.
 
C

cpisz

cpisz <[email protected]> kirjutas:







And so?


The order of destruction of what objects you are talking? Not the
singleton, I suppose, as this is left undestructed.

Paavo- Hide quoted text -

- Show quoted text -- Hide quoted text -

- Show quoted text -



The static pointer is destroyed, lost, zapped, before or after the
class making a call with it.
I don't care if the singleton was never destroyed, it doesnt matter.
 
C

cpisz

Nothing happens to the static pointer. It is not lost nor zapped,
whatever you might mean with that. The memory page containing the pointer
is unmapped from the process virtual memory space by the OS upon program
exit, but by that time the process code is not executing any more and has
no chance to notice that.

Errors do not have to occur in the process code to be errors. In a
linux environment, the result was a seg fault, which your boss will
not be pleased with no matter how much tech talk you give him.
Only if the pointer is inside a dynamically loaded library, one can have
problems if the library is unloaded from the process memory space while
some other code is still making use of it - but in this case one already
has way larger problems, for example the Instance() member function code
is also unmapped, along with all other functions from that library.
However, as the order of dynamic libraries' unload is well defined one
can take care of such problems properly.

My debugger disagrees with you. I'll give you a compilable example as
soon as I get VS fixed later today.
 
C

cpisz

Good! Please do so!



class Singleton
{
public:

static Singleton & Instance()
{
if( !m_instance )
{
m_instance = new Singleton();
}

return *m_instance;
}

static void DoStuff()
{
int x = 1 + 1;
}

private:

static Singleton * m_instance;
};

Singleton * Singleton::m_instance = 0;

class Foo
{
public:

Foo(){}
~Foo()
{
Singleton::Instance().DoStuff();
}
};

int main()
{
static Foo foo;

return 0; // Undefined behavior after this line, when program
cleanup occurs!
}



static de-initialization fiasco.

If you were the only person working on a project, it is avoidable as
long as you are careful. However, I've yet to land a job where I was
the one and only programmer to ever touch the code, nor do I think I'd
want to. You will undoubtedly run into someone that directly or
indirectly creates this scenario when you introduce singletons into
your project. Thus, my argument is that it is far simpler and less
error prone to simply pass a reference to the would be singleton
object around to those who need it and check the address when needed.
 
F

Francesco S. Carta

class Singleton
{
public:

static Singleton & Instance()
{
if( !m_instance )
{
m_instance = new Singleton();
}

return *m_instance;
}

static void DoStuff()
{
int x = 1 + 1;
}

private:

static Singleton * m_instance;

};

Singleton * Singleton::m_instance = 0;

class Foo
{
public:

Foo(){}
~Foo()
{
Singleton::Instance().DoStuff();
}

};

int main()
{
static Foo foo;

return 0; // Undefined behavior after this line, when program
cleanup occurs!

}

static de-initialization fiasco.

Sorry for dropping in, maybe I have not the right grip on the subject
but I want to show what I understand about your program, and take the
chance to learn something new.

If I get it right, this is the sequence:

--- start of execution
- m_instance gets created
- foo gets created
- main returns
- foo starts being destroyed
- a Singleton gets created
- x gets created
- x gets destroyed
- foo finishes its destruction
- m_instance gets destroyed
--- end of execution

Have I got it right? How that relates to the de-initialization fiasco?

Don't take me wrong, those are sincere questions.

Have good time,
Francesco
 
C

cpisz

class Singleton
Sorry for dropping in, maybe I have not the right grip on the subject
but I want to show what I understand about your program, and take the
chance to learn something new.

If I get it right, this is the sequence:

--- start of execution
- m_instance gets created
- foo gets created

The order of initialization is not defined. foo might get created
before m_instance or vica versa.

- main returns
Yes

- foo starts being destroyed
- a Singleton gets created

Maybe, maybe not. The order of deinitialization is also undefined.

- x gets created
- x gets destroyed

This will happen only when foo gets destroyed, when that happens isn't
defined
- foo finishes its destruction
yes

- m_instance gets destroyed

Whether or not this occurs before foo is destroyed is undefined. That
is the problem and is the de-initialization fiasco.
--- end of execution

Have I got it right? How that relates to the de-initialization fiasco?

Don't take me wrong, those are sincere questions.
-


The problem is there are two variables in static or global space. The
destruction of one relies on the other existing. Since, the order of
de-initialization is undefined, one cannot guarentee the other exists
at that point. This occurs after the return 0; statement in main,
which makes it a hell of a thing to debug if you don't know what the
problem is already. Especially, in a more complicated scenario where
the return of GetInstance might be used indirectly (such as someone
storing it or deep in a call stack)

The way to avoid this is to make sure that no global or static depends
on another in its destruction, which I argue is fine and dandy on a
small project or one that you write completely alone, but I have found
in practice (5 out of 6 jobs) that it will indeed cause a problem at
some point.

The worst of it is you will have to convince all your peers it is a
problem! Sometimes the program will execute and exit without any
indication of a problem. Then one day someone adds a line somewhere
completely unrelated, and blamo, it doesn't run anymore. It can also
run on one persons machine while not running on another's! Mix that
with working with people who have 30+ years on you and have no respect
for your "c++ ways" and you will have one ton of work frustration.

It is much easier for everyone to simply pass a reference to the would
be singleton around. After all how much of a difference is there in a
caveat that says" There should only be one instance of this object"
and a caveat that says "We must check all dependencies on this
singleton at all times as it relates to initializiation and de-
initialization". I beleive the former is simpler to understand and
enforce for all parties involved. 80% of your peers might not even
know what a singleton is much less what the de-initialization fiasco
is. Hopefully, they all understand what a reference is and how to use
it.
 
F

Francesco S. Carta

The order of initialization is not defined. foo might get created
before m_instance or vica versa.

I'm not so sure, but I think that global objects get created first of
anything else, while static objects get created the first time that
the function they appear in gets called. If you have "void f()
{ static Foo foo(); }" and you don't call f(), then foo shouldn't be
created at all, I think.
Maybe, maybe not. The order of deinitialization is also undefined.

Wait, "new Singleton" gets executed only the first time that Instance
() gets called, and Instance() gets called for the first time within
the Foo dtor (I might have worded it badly, with "starts being
destroyed" I meant "the dtor starts executing"). If I got it right,
the order of the above should be well defined.
This will happen only when foo gets destroyed, when that happens isn't
defined

At this point I think I should throw away my book. I've been taught
that local, non-static variables get created and destructed at every
function call, and x is a local, non-static variable of DoStuff().
Whether or not this occurs before foo is destroyed is undefined. That
is the problem and is the de-initialization fiasco.

Taking for granted that I haven't completely misunderstood the
construction-destruction orders, the first object to be created should
be the last to be destroyed (that's the logic I followed while writing
the sequence above).

I think I need some of those +30y experienced people to throw some
light on this - I'm 30yo, some 60yo around?

[ I would address the paragraphs of yours that I snipped, but I
wouldn't feel on firm ground, sorry, I prefer to stop here ]

In impatient waiting,
Francesco
 
F

Francesco S. Carta

I'm not so sure, but I think that global objects get created first of
anything else, while static objects get created the first time that
the function they appear in gets called. If you have "void f()
{ static Foo foo(); }" and you don't call f(), then foo shouldn't be
created at all, I think.

Typo there... that should read "void f() { static Foo foo; }" (without
the parentheses) of course.

Francesco
 
T

Thomas J. Gritzan

cpisz said:
The order of initialization is not defined. foo might get created
before m_instance or vica versa.

Wrong (the last sentence). m_instance is initialized to zero before
main, but foo is constructed inside of main.
Maybe, maybe not. The order of deinitialization is also undefined.

It is undefined, but m_instance and the object it points to are never
destroyed. Their memory are release after the end of the program, when
there's no more program code running.
This will happen only when foo gets destroyed, when that happens isn't
defined


Whether or not this occurs before foo is destroyed is undefined. That
is the problem and is the de-initialization fiasco.

m_instance is never destroyed. The pointee is never destroyed, too. Only
its memory will be released.
The problem is there are two variables in static or global space. The
destruction of one relies on the other existing.

There's a pointer that has no destructor, so no code is running for its
destruction.
I don't see any race condition in this example code.
 
C

cpisz

Wait, "new Singleton" gets executed only the first time that Instance
() gets called, and Instance() gets called for the first time within
the Foo dtor

All true. But does Foo dtor occur before or after the static pointer
m_instance is "destructed" for lack of a better word. That is what is
not defined.

At this point I think I should throw away my book. I've been taught
that local, non-static variables get created and destructed at every
function call, and x is a local, non-static variable of DoStuff().

Your book is correct. We are not worried about x.
The variables of interest in this problem are foo and m_instance.
x was only inserted to examplify that DoStuff does something.


Taking for granted that I haven't completely misunderstood the
construction-destruction orders, the first object to be created should
be the last to be destroyed (that's the logic I followed while writing
the sequence above).

You don't create a global or static variable. It is created for you
before main is even entered. Likewise they are destroyed for you after
main exits. The order in which they are created and destroyed is not
defined.

int x;
int y;

int main()
{
int z;
return 0;
}

x may be created before y. y may be created before x.
x may be destroyed before y. y may be destroyed before x.
The only thing you do know is that both x and y will be created before
z... and that x and y will be destroyed after z.

now:

static Singleton * Singleton::m_instance = 0;

m_instance has the same caveats as x or y in the previous example.

Furthermore:

static Singleton * Singleton::m_instance = 0;
static Foo foo;

foo has the same caveats as x or y.

I may have made an error in my example in the previous post. Let us
for the sake of argument move "static Foo foo;" outside of main, or
hell it could just be "Foo foo;" where foo is a global.

















I think I need some of those +30y experienced people to throw some
light on this - I'm 30yo, some 60yo around?

[ I would address the paragraphs of yours that I snipped, but I
wouldn't feel on firm ground, sorry, I prefer to stop here ]

In impatient waiting,
Francesco
--
 Francesco S. Carta, hobbyist
 http://fscode.altervista.org- Hide quoted text -

- Show quoted text -
 
C

cpisz

I may have made an error in my example in the previous post. Let us
for the sake of argument move "static Foo foo;" outside of main, or
hell it could just be "Foo foo;" where foo is a global.

New source listing to examplify the problem:



class Singleton
{
public:

static Singleton & Instance()
{
if( !m_instance )
{
m_instance = new Singleton();
}

return *m_instance;
}

static void DoStuff()
{
int x = 1 + 1;
}

private:

static Singleton * m_instance;
};

Singleton * Singleton::m_instance = 0;

class Foo
{
public:

Foo(){}
~Foo()
{
Singleton::Instance().DoStuff();
}
};

Foo foo;

int main()
{
return 0; // Undefined behavior after this line, when program
cleanup occurs!
}
 
T

Thomas J. Gritzan

cpisz said:
All true. But does Foo dtor occur before or after the static pointer
m_instance is "destructed" for lack of a better word. That is what is
not defined.

The pointer is never destructed.
Its holding memory is released (maybe by the operation system) _after_
any cleanup code runs.
You don't create a global or static variable. It is created for you
before main is even entered. Likewise they are destroyed for you after
main exits. The order in which they are created and destroyed is not
defined.

Thats true, but build-in types (pointers, ints, ...) aren't destroyed.
 
F

Francesco S. Carta

All true. But does Foo dtor occur before or after the static pointer
m_instance is "destructed" for lack of a better word. That is what is
not defined.


Your book is correct. We are not worried about x.
The variables of interest in this problem are foo and m_instance.
x was only inserted to examplify that DoStuff does something.


You don't create a global or static variable. It is created for you
before main is even entered. Likewise they are destroyed for you after
main exits. The order in which they are created and destroyed is not
defined.

I've just grabbed my book and I have read some parts again. Static
class members are considered just like global objects, that is,
they're considered not local. And they are guaranteed to be created
before main() starts. This ensures that m_instance gets created before
foo.

Similarly, I think I'm guaranteed that m_instance will be destroyed
(i.e. its memory freed) after all local objects, in this case foo.

This all seems well-defined construction-destruction order here to me.
int x;
int y;

int main()
{
   int z;
   return 0;

}

x may be created before y. y may be created before x.
x may be destroyed before y. y may be destroyed before x.

If that was true, I could I ever write something like:
-------
int x = 0;
int y = x;
-------
?

Seems to me that the order of construction is well defined here too.
The only thing you do know is that both x and y will be created before
z... and that x and y will be destroyed after z.

now:

static Singleton * Singleton::m_instance = 0;

m_instance has the same caveats as x or y in the previous example.

Furthermore:

static Singleton * Singleton::m_instance = 0;
static Foo foo;

foo has the same caveats as x or y.

I may have made an error in my example in the previous post. Let us
for the sake of argument move "static Foo foo;" outside of main, or
hell it could just be "Foo foo;" where foo is a global.

In such a case I wouldn't really be able to tell. Maybe they will be
constructed depending on the order of their declarations (and
destroyed in the inverse order) or maybe globals will be constructed
before static class members. I don't really know.

All I know is that your previous example seems not to raise any issue
anymore to my eyes, reading my book gave me some more surety on that.

I curious to discover what your last modification (creating a global
"Foo foo;") happens to behave wrt the standard specs.

Interesting stuff.

Francesco
 

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

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,813
Latest member
lawrwtwinkle111

Latest Threads

Top