References, AddressOf and Object management

T

Tim Clacy

Am I correct in think that you can't re-assign a reference to a different
object? If so, what should happen here:

struct A
{
DoSomeThing();
};

A& GetNextA();

while (1)
{
A& a = GetNextA();

a.DoSomeThing();
}

Does reference 'a' only get assigned first time around the loop and,
thereafter, object 'a' gets assigned to the next 'A'... or is 'a' connected
to a different 'A' every time around the loop? What about this:

while (1)
{
{
A& a = GetNextA();

a.DoSomeThing();
}
}

Surely, 'a' would now be different every time around the loop (because the
scope of 'a' ends before looping?



On a related note, is it possible to prevent a reference to a class
instance? Similarly, is it possible to prevent the address of an instance
from being taken?

What I'm looking for is a way to implement a 'Reference<T>' class. In other
words, some kind of class like a smart-pointer except that the 'Reference'
is the only way to access the object to which it refers. I want to prevent
'address-of' and references and use the 'Reference' as a means to manage
object life-time.

Any insight or links to good articles will be very much appreciated.

Regards


Tim
 
R

Ron Natalie

Tim Clacy said:
while (1)
{
A& a = GetNextA();

a.DoSomeThing();
}

Since "a" is not declared static, it gets initalized everytime code passes through
the block.
while (1)
{
{
A& a = GetNextA();

a.DoSomeThing();
}
}

The extra { } have no additional effect here.
On a related note, is it possible to prevent a reference to a class
instance? Similarly, is it possible to prevent the address of an instance
from being taken?

You can't do anything to prevent a value from being bound to a reference.
You can make taking the address of it difficult, by overloading the unary
& operator for the class, but I'm not certain that would be a fool proof
way of disabling it entirely.
What I'm looking for is a way to implement a 'Reference<T>' class. In other
words, some kind of class like a smart-pointer except that the 'Reference'
is the only way to access the object to which it refers. I want to prevent
'address-of' and references and use the 'Reference' as a means to manage
object life-time.

I'm unclear of what you're really trying to accomplish, however provided you don't
specifically provide a way to do with it, a Reference<T> wouldn't be bindable to T&.
However such a class would have other problems. Specifically, you can't overload
operator (period). .
 
P

Pete Vidler

Tim said:
Am I correct in think that you can't re-assign a reference to a different
object? If so, what should happen here:
Yes.

[snip]
{
A& a = GetNextA();

a.DoSomeThing();
}

The object "a" only exists within this scope. Once the scope is left it
should be destroyed. When the loop re-enters the scope a new object,
also called "a", will be created.

I suppose compiler optimisations might change this behaviour in some
unnoticeable way.

Note that I'm not certain about any of this. You could always try it and
see what your compiler does.
Does reference 'a' only get assigned first time around the loop and,
thereafter, object 'a' gets assigned to the next 'A'... or is 'a' connected
to a different 'A' every time around the loop? What about this:

while (1)
{
{
A& a = GetNextA();

a.DoSomeThing();
}
}

Call me crazy, but isn't that identical to the last bit of code? You're
just adding an extra (redundant) scope.
Surely, 'a' would now be different every time around the loop (because the
scope of 'a' ends before looping?

Yes, I'm pretty sure they are the same.
On a related note, is it possible to prevent a reference to a class
instance? Similarly, is it possible to prevent the address of an instance
from being taken?

What I'm looking for is a way to implement a 'Reference<T>' class. In other
words, some kind of class like a smart-pointer except that the 'Reference'
is the only way to access the object to which it refers. I want to prevent
'address-of' and references and use the 'Reference' as a means to manage
object life-time.

I do this by writing public static "Create" methods in each class that
return a smart pointer to the new object. All constructors are
private/protected so there is no way of creating the object outside of
the static methods. Consequently there is no direct access to the object
itself (only through the smart pointer), so a reference or pointer to it
cannot be obtained (unless the smart pointer allows it).

Like this:

class SafeObject
{
public:
static MySmartPtr< SafeObject > Create();

private:
SafeObject() {}

// Probably don't need private copy-ctors or copy-assignment
// operators, because you can't access the object directly
// to use them with anyway.
};

Where "MySmartPtr" is your "Reference" class template. You will be able
to take a reference or pointer of MySmartPtr, but not of SafeObject
directly (to the best of my knowledge).

Hope that helps.

-- Pete
 
T

Tim Clacy

Pete said:
Tim said:
Am I correct in think that you can't re-assign a reference to a
different object? If so, what should happen here:
Yes.

[snip]
{
A& a = GetNextA();

a.DoSomeThing();
}

The object "a" only exists within this scope. Once the scope is left
it should be destroyed. When the loop re-enters the scope a new
object,
also called "a", will be created.

Pete,

Hi. So when you loop, this is like leaving a scope then re-entering?
I suppose compiler optimisations might change this behaviour in some
unnoticeable way.

Note that I'm not certain about any of this. You could always try it
and
see what your compiler does.


Call me crazy, but isn't that identical to the last bit of code?
You're
just adding an extra (redundant) scope.


Yes, I'm pretty sure they are the same.

Yep, it sounds like they are the same. I wasn't (still not) entirely clear
about life-time of variables/references/objects inside a loop scope. If
variables/references/objects are destroyed and re-constructed every loop
cycle, then my second case is the same as the first.
I do this by writing public static "Create" methods in each class that
return a smart pointer to the new object. All constructors are
private/protected so there is no way of creating the object outside of
the static methods. Consequently there is no direct access to the
object itself (only through the smart pointer), so a reference or
pointer to it cannot be obtained (unless the smart pointer allows it).

Like this:

class SafeObject
{
public:
static MySmartPtr< SafeObject > Create();

private:
SafeObject() {}

// Probably don't need private copy-ctors or copy-assignment
// operators, because you can't access the object directly
// to use them with anyway.
};

Where "MySmartPtr" is your "Reference" class template. You will be
able
to take a reference or pointer of MySmartPtr, but not of SafeObject
directly (to the best of my knowledge).

Hope that helps.

Hmm, that's about where I am right now... but all we've done is replaced the
problem of controlling the object life-time with a problem of controlling
the smart-pointer life-time.
 
T

Tim Clacy

Ron said:
Since "a" is not declared static, it gets initalized everytime code
passes through
the block.


The extra { } have no additional effect here.


You can't do anything to prevent a value from being bound to a
reference.

Ron,

Hi. Doesn't this mean that there is no automatic way to manage object
life-time (because anyone can just connect a C++ reference to any object)?
You can make taking the address of it difficult, by overloading the
unary & operator for the class, but I'm not certain that would be a
fool proof
way of disabling it entirely.

I'm unclear of what you're really trying to accomplish, however
provided you don't
specifically provide a way to do with it, a Reference<T> wouldn't be
bindable to T&.
However such a class would have other problems. Specifically, you
can't overload
operator (period). .

....hmm, so the long and the short of it is that if you don't mind using '->'
to access objects you can almost acheive automatic object life-time, but if
you want to use '.' you have to use C++ references, which are an
object-life-time management nightmare?

Regards


Tim
 
P

Pete Vidler

Tim Clacy wrote:
[snip]
Hi. So when you loop, this is like leaving a scope then re-entering?

Effectively, yes.

[snip]
Yep, it sounds like they are the same. I wasn't (still not) entirely clear
about life-time of variables/references/objects inside a loop scope. If
variables/references/objects are destroyed and re-constructed every loop
cycle, then my second case is the same as the first.

Of course, I'm not exactly certain about this. Consider the fact that,
in a "for( int i =... )" loop, the i variable is considered to be inside
the loop's scope. It doesn't get deleted and reconstructed each time,
though.

I'm just confusing myself now. In reality all this is rarely an issue.
If you need a variable in every loop cycle, declare it outside the loop.

[snip]
Hmm, that's about where I am right now... but all we've done is replaced the
problem of controlling the object life-time with a problem of controlling
the smart-pointer life-time.
[snip]

Smart pointers are typically reference counted. If all you really want
is a smart pointer, check out boost::shared_ptr from www.boost.org. I
can highly recommend it (and the rest of that library).

As an aside about shifting the problem onto a separate class, this is a
common technique in C++. The answer to many problems seems to be adding
an extra layer of indirection (so I've heard).

-- Pete
 
B

Buster

Pete said:
Tim Clacy wrote:

[snip]
Hi. So when you loop, this is like leaving a scope then re-entering?

Effectively, yes.

[snip]
Yep, it sounds like they are the same. I wasn't (still not) entirely
clear
about life-time of variables/references/objects inside a loop scope. If
variables/references/objects are destroyed and re-constructed every loop
cycle, then my second case is the same as the first.

Of course, I'm not exactly certain about this. Consider the fact that,
in a "for( int i =... )" loop, the i variable is considered to be inside
the loop's scope. It doesn't get deleted and reconstructed each time,
though.

There are two nested scopes associated with for loops: an outer scope
for any declaration-definition in the 'for' initializer, and an inner
scope for the loop body. The inner scope may or may not be marked
explicitly with braces but it's always there. The outer scope is entered
once at the beginning of the construct and left once after all
iterations are complete. The inner scope is entered and left on every
iteration.

For while loops, the scope of a declaration in the 'while' condition
contains the scope of the loop body and is entered and left once for
every iteration. It's less important here to make the 'two scopes'
distinction. The same is true of declarations in 'if' conditions.
('switch' too?)

You can depend on this absolutely. Optimization won't change the meaning
of your code. The one small snag is that there are compilers that use
obsolete rules for the scope of declarations in for, while and if
statements. Hopefully it's no longer necessary to think about that.

Regards,
Buster.
 
T

Tim Clacy

Pete said:
Tim Clacy wrote:
[snip]
Hi. So when you loop, this is like leaving a scope then re-entering?

Effectively, yes.

[snip]
Yep, it sounds like they are the same. I wasn't (still not) entirely
clear about life-time of variables/references/objects inside a loop
scope. If variables/references/objects are destroyed and
re-constructed every loop cycle, then my second case is the same as
the first.

Of course, I'm not exactly certain about this. Consider the fact that,
in a "for( int i =... )" loop, the i variable is considered to be
inside
the loop's scope. It doesn't get deleted and reconstructed each time,
though.

I'm just confusing myself now. In reality all this is rarely an issue.
If you need a variable in every loop cycle, declare it outside the
loop.

[snip]
Hmm, that's about where I am right now... but all we've done is
replaced the problem of controlling the object life-time with a
problem of controlling the smart-pointer life-time.
[snip]

Smart pointers are typically reference counted. If all you really want
is a smart pointer, check out boost::shared_ptr from www.boost.org. I
can highly recommend it (and the rest of that library).

I've have perused Boost (some of the 15MB) and there's some awfully clever
stuff in there. However, what's to stop someone taking a C++ reference to a
smart-pointer (i.e. defeating the purpose)?
 
T

Tim Clacy

Buster said:
Pete said:
Tim Clacy wrote:

[snip]
Hi. So when you loop, this is like leaving a scope then re-entering?

Effectively, yes.

[snip]
Yes, I'm pretty sure they are the same.

Yep, it sounds like they are the same. I wasn't (still not) entirely
clear
about life-time of variables/references/objects inside a loop
scope. If variables/references/objects are destroyed and
re-constructed every loop cycle, then my second case is the same as
the first.

Of course, I'm not exactly certain about this. Consider the fact
that, in a "for( int i =... )" loop, the i variable is considered to
be inside the loop's scope. It doesn't get deleted and reconstructed
each time, though.

There are two nested scopes associated with for loops: an outer scope
for any declaration-definition in the 'for' initializer, and an inner
scope for the loop body. The inner scope may or may not be marked
explicitly with braces but it's always there. The outer scope is
entered once at the beginning of the construct and left once after all
iterations are complete. The inner scope is entered and left on every
iteration.

For while loops, the scope of a declaration in the 'while' condition
contains the scope of the loop body and is entered and left once for
every iteration. It's less important here to make the 'two scopes'
distinction. The same is true of declarations in 'if' conditions.
('switch' too?)

You can depend on this absolutely. Optimization won't change the
meaning of your code. The one small snag is that there are compilers
that use obsolete rules for the scope of declarations in for, while
and if statements. Hopefully it's no longer necessary to think about
that.

Regards,
Buster.

Cheers.
 
A

Alf P. Steinbach

* "Tim Clacy said:
Hi. Doesn't this mean that there is no automatic way to manage object
life-time (because anyone can just connect a C++ reference to any object)?

In theory yes. In practice no. Both because code that does that breaks
the contract, and because you _can_ (although no one does) make it
utterly impractical to obtain a direct reference.
 
P

Pete Vidler

Tim Clacy wrote:
[snip]
I've have perused Boost (some of the 15MB) and there's some awfully clever
stuff in there. However, what's to stop someone taking a C++ reference to a
smart-pointer (i.e. defeating the purpose)?
[snip]

Nothing prevents it. It just isn't a problem.

Unless you have a specific situation where it could be a problem?

-- Pete
 
J

Jeff Flinn

Tim Clacy said:
Pete said:
Tim Clacy wrote:
[snip]
Hi. So when you loop, this is like leaving a scope then re-entering?

Effectively, yes.

[snip]
Smart pointers are typically reference counted. If all you really want
is a smart pointer, check out boost::shared_ptr from www.boost.org. I
can highly recommend it (and the rest of that library).

I've have perused Boost (some of the 15MB) and there's some awfully clever
stuff in there. However, what's to stop someone taking a C++ reference to a
smart-pointer (i.e. defeating the purpose)?

How? Can you show a case where it's an issue? A typical usage would be:

struct A
{
...

bool GotMilk()const{ return true; }
};

void somefunction( A& aRef )
{
bool lGood = aRef.GotMilk():
}

void SomeOtherFunction()
{
boost::shared_ptr<A> lAPtr( new A );

...

somefunction( *lAPtr ); // reference valid for the
// duration of somefunction
// indeed for this entire scope
...

} //

Jeff F
 
T

Tim Clacy

Alf said:
In theory yes. In practice no. Both because code that does that
breaks the contract, and because you _can_ (although no one does)
make it
utterly impractical to obtain a direct reference.

Alf,

Hi. How can one make it impractical to obtain a direct reference to an
object; others are saying that you can't do that?


Tim
 
T

Tim Clacy

Pete said:
Tim Clacy wrote:
[snip]
I've have perused Boost (some of the 15MB) and there's some awfully
clever stuff in there. However, what's to stop someone taking a C++
reference to a smart-pointer (i.e. defeating the purpose)?
[snip]

Nothing prevents it. It just isn't a problem.

....hmm, don't you think robust object life-time management is just about the
most important thing to get right to help ensure a robust, quality product?
My experience is that if something is fragile, sooner or later, it will
break... but perhaps I've just been unlucky?
 
T

Tim Clacy

Jeff said:
Tim Clacy said:
Pete said:
Tim Clacy wrote:
[snip]
Hi. So when you loop, this is like leaving a scope then
re-entering?

Effectively, yes.

[snip]
Smart pointers are typically reference counted. If all you really
want is a smart pointer, check out boost::shared_ptr from
www.boost.org. I can highly recommend it (and the rest of that
library).

I've have perused Boost (some of the 15MB) and there's some awfully
clever stuff in there. However, what's to stop someone taking a C++
reference to a smart-pointer (i.e. defeating the purpose)?

How? Can you show a case where it's an issue? A typical usage would
be:

struct A
{
...

bool GotMilk()const{ return true; }
};

void somefunction( A& aRef )
{
bool lGood = aRef.GotMilk():
}

void SomeOtherFunction()
{
boost::shared_ptr<A> lAPtr( new A );

...

somefunction( *lAPtr ); // reference valid for the
// duration of somefunction
// indeed for this entire scope

What if somefunction assigns a static or class member to that reference...
or copies it to another thread?
 
P

Pete Vidler

Tim Clacy wrote:
[snip]
...hmm, don't you think robust object life-time management is just about the
most important thing to get right to help ensure a robust, quality product?
My experience is that if something is fragile, sooner or later, it will
break... but perhaps I've just been unlucky?
[snip]

Yes, but using a smart pointer is just about as robust as it gets. You
still haven't explained how it isn't robust -- there's nothing wrong
with taking a reference to the smart pointer that I can see. So long as
you don't mess with raw pointers your app will be perfectly robust.

The only problem would be if you could assing a temporary object to a
reference, but I'm pretty sure the standard forbids it. You can assign
temporaries to const references, but then the lifetime of the temporary
is guaranteed to be at least as long as the lifetime of the const
reference (I believe).

-- Pete
 
P

Pete Vidler

Tim Clacy wrote:
[snip]
What if somefunction assigns a static or class member to that reference...
or copies it to another thread?
[snip]

In the case of a programmer assigning static members to references, the
fool deserves everything he gets. You can protect programmers from
themselves only so far.

In the given example you cannot reassign a reference. It just isn't
legal C++. You can alter the object that the reference is of, in ways
determined by the interface of its class (providing it's a non-const
reference), but that's about it.

-- Pete
 
T

Tim Clacy

Pete Vidler said:
Tim Clacy wrote:
[snip]
...hmm, don't you think robust object life-time management is just about the
most important thing to get right to help ensure a robust, quality product?
My experience is that if something is fragile, sooner or later, it will
break... but perhaps I've just been unlucky?
[snip]

Yes, but using a smart pointer is just about as robust as it gets. You
still haven't explained how it isn't robust -- there's nothing wrong
with taking a reference to the smart pointer that I can see. So long as
you don't mess with raw pointers your app will be perfectly robust.

I'm almost convinced... provided that it is possible to ensure that only the
smart pointer can do the creation of the object and that it's not possible
to 'new' smart pointers (so the life-time of the smart-pointer is under
control) and it's not possible to take the address of smart pointers. That
should all be acheivable shouldn't it?

I think I'll spend the weekend studying Boost's smart pointer.

Cheers
 
A

Alf P. Steinbach

* "Tim Clacy said:
How can one make it impractical to obtain a direct reference to an
object

This was discussed in
<url:
http://www.google.com/[email protected]>

Summary of proposed solutions in the context of preventing deletion via
a raw pointer obtained from a smart-pointer class:

Andrei Alexandrescu, Thomas Mang, Steve Dewhurst:
Let operator-> return a proxy (and Andrei, of course, further proposed
using a recursive template to generate a proxy to proxy to proxy..., say
to 100 levels, to really make it hard for client code to get the address).

Paavo Helde:
If you own the implementation of the class A that the smart pointer
encapsulates, let its destructor be protected and let the smart
pointer's operator-> return a pointer to a derived class instance.

Niklas Borson:
Let the smart pointer be a wrapper class with one wrapper function
for each function of the wrapped class, instead of using operator->.
 
P

Pete Vidler

Tim Clacy wrote:
[snip]
I'm almost convinced... provided that it is possible to ensure that only the
smart pointer can do the creation of the object and that it's not possible
to 'new' smart pointers (so the life-time of the smart-pointer is under
control) and it's not possible to take the address of smart pointers. That
should all be acheivable shouldn't it?
[snip]

The basics are straight from the FAQ:

class SafeClass
{
public:
typedef boost::shared_ptr< SafeClass > Ptr;

// Might need a static_cast here, I don't remember exactly.
Ptr Create() { return new SafeClass; }

private:
SafeClass() { ... }
};

// This should be the only way to create a SafeClass object:
SafeClass::ptr safeClass1 = SafeClass::Create();

This meets my top two criteria -- safe and readable.

In the thread "Base class method that returns a pointer to a derived
class?" on this newsgroup I've detailed a technique for extending this
to a hierarchy (from Maxim Yegorushkin on comp.lang.c++.moderated).

-- Pete
 

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,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top