Topic: reference classes, and operator.()
Author: jimad@microsoft.UUCP (Jim ADCOCK)
Date: 19 Sep 90 19:41:45 GMT Raw View
In article <8445@jarthur.Claremont.EDU> dfoster@jarthur.Claremont.EDU (Derek R. Foster) writes:
|Is there any way to change the variable referenced by a reference variable
|after the reference variable has been initialized?
|
|In particular, I have a class which is passed a reference to a target
|variable which it is to modify. I would like to be able to simply
|declare a class member variable which would be a reference to the
|target variable, thus allowing my class member operations to access
|the target variable as if it was simply a member variable. In other words,
|I would like to be able to 'forget' that the target variable is actually
|external to the particular class object which has been told to modify it,
|except for in the routines which change to a new target. This seems like
|a perfectly legitimate use of a reference variable, and would work quite
|nicely if I was able to specify the target variable at the time a class
|object was constructed. Unfortunately, I can't.....
/****
In valid C++, references cannot be changed after being initialized. Compilers
can perform significant optimizations on references, due to the fact that
references cannot be aliased [they have no address, they can alias what they
refer to, but cannot be themselves aliased] So, its not "legitimate" to try
to "reassign" references via hackery. However, one can construct one's own
reference class, that internally uses a pointer to store the reference, but
presents that pointer to the outside world as if a reference. Then you
just create an access member function to allow the internal pointer to be
reassigned to refer to a new object. Unfortunately, when you try to do this,
you'll find that while C++ allows pointer classes to overload operator->(),
C++ ***does not*** allow reference classes the equivalent option of overloading
operator.() To get around this problem, one is forced to provide a forwarding
definition for all the public members of the class being referenced. This,
in turn, generally prevents reference classes from being implemented via macros
or templates.
-- Please join me in lobbying the ANSI C++ committee to correct this oversight
in the language definition. Overloading operator.() makes equal sense for
reference classes as overloading operator->() for pointer classes. Classes
that naturally follow value semantics need to be implementable using
reference syntax, not pointer syntax, otherwise the usage becomes
horrible, as you point out. --
The code below illustrates this problem, and the painful work-around.
****/
extern "C"
{
#include <stdio.h>
}
class THING
{
char* name;
public:
THING(char* nameT) : name(nameT) {}
void DoSomething() { printf("%s DoSomething()\n", name); }
void DoSomething2() { printf("%s DoSomething2()\n", name); }
};
typedef THING* PTHING;
typedef THING& RTHING;
// To make the below classes generally useful, you'd probably want to
// implement them as templates or macros, so that they can be parameterized
// on the type of object being referred to....
// Yes! it's "trivial" to make a pointer class....
class THING_PTR
{
PTHING pthing;
public:
THING_PTR() { pthing = 0; }
THING_PTR(RTHING rthing) { pthing = &rthing; }
// ....because operator->() ***is*** overloadable.
PTHING operator->() { return pthing; }
RTHING operator*() { return *pthing; }
operator PTHING() { return pthing; }
void NowReferences(RTHING rthing) { pthing = &rthing; }
// ....
};
// But, is it trivial to make a reference class? ....
class THING_REF
{
PTHING pthing;
public:
THING_REF() { pthing = 0; }
THING_REF(RTHING rthingT) { pthing = &rthingT; }
// ....but nooo, because operator.() ***isn't*** overloadable!
// RTHING operator.() { return *pthing; }
operator RTHING() { return *pthing; }
void NowReferences(RTHING rthingT) { pthing = &rthingT; }
// ....
};
// So, to make a legitimate reference class one has to write a forwarding method
// for every method in the referenced class -- which in turn means the
// referencing class can't be implemented using macros or templates!
class PAINFUL_THING_REF
{
PTHING pthing;
public:
PAINFUL_THING_REF() { pthing = 0; }
PAINFUL_THING_REF(RTHING rthingT) { pthing = &rthingT; }
operator RTHING() { return *pthing; }
void NowReferences(RTHING rthingT) { pthing = &rthingT; }
// ....
// Now, add a forwarding call definition for ***ALL*** of THING's methods
// ....In this case, just two, thank god....
void DoSomething() { pthing->DoSomething(); }
void DoSomething2() { pthing->DoSomething2(); }
};
main()
{
THING thing1("thing1");
THING thing2("thing2");
thing1.DoSomething();
thing2.DoSomething();
putchar('\n');
THING_PTR thingPtr(thing1);
thingPtr->DoSomething();
thingPtr.NowReferences(thing2);
thingPtr->DoSomething();
putchar('\n');
// Too bad, **someone** decided you can't overload operator, so you
// can't do this:
printf("Sorry: THING_REF doesn't work....\n");
printf(" ....because operator.() isn't implemented yet!\n\n");
#if 0
THING_REF thingRef(thing1);
thingRef.DoSomething();
thingRef.NowReferences(thing2);
thingRef.DoSomething();
putchar('\n');
#endif
// -- Except, via the painful class implementation:
PAINFUL_THING_REF thingRef(thing1);
thingRef.DoSomething();
thingRef.NowReferences(thing2);
thingRef.DoSomething();
putchar('\n');
}