jimad@microsoft.UUCP (Jim ADCOCK) (09/20/90)
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'); }