How can I change the variable to which a C++ reference refers?
If I have this:
int a = 2;
int b = 4;
int &ref = a;
开发者_运维百科How can I make ref
refer to b
after this code?
This is not possible, and that's by design. References cannot be rebound.
With C++11 there is the new(ish) std::reference_wrapper.
#include <functional>
int main() {
int a = 2;
int b = 4;
auto ref = std::ref(a);
//std::reference_wrapper<int> ref = std::ref(a); <- Or with the type specified
ref = std::ref(b);
}
This is also useful for storing references in containers.
You can't reassign a reference, but if you're looking for something that would provide similar abilities to this you can do a pointer instead.
int a = 2;
int b = 4;
int* ptr = &a; //ptr points to memory location of a.
ptr = &b; //ptr points to memory location of b now.
You can get or set the value within pointer with:
*ptr = 5; //set
int c = *ptr; //get
Formally speaking, that is impossible as it is forbidden by design. Arbitrarily speaking, that is possible.
A references is stored as a pointer, so you can always change where it points to as long as you know how to get its address. Similarly, you can also change the value of const variables, const member variables or even private member variables when you don't have access to.
For example, the following code has changed class A's const private member reference:
#include <iostream>
using namespace std;
class A{
private:
const int &i1;
public:
A(int &a):i1(a){}
int geti(){return i1;}
int *getip(){return (int*)&i1;}
};
int main(int argc, char *argv[]){
int i=5, j=10;
A a(i);
cout << "before change:" << endl;
cout << "&a.i1=" << a.getip() << " &i=" << &i << " &j="<< &j << endl;
cout << "i=" << i << " j=" <<j<< " a.i1=" << a.geti() << endl;
i=6; cout << "setting i to 6" << endl;
cout << "i=" << i << " j=" <<j<< " a.i1=" << a.geti() << endl;
*(int**)&a = &j; // the key step that changes A's member reference
cout << endl << "after change:" << endl;
cout << "&a.i1=" << a.getip() << " &i=" << &i << " &j="<< &j << endl;
cout << "i=" << i << " j=" <<j<< " a.i1=" << a.geti() << endl;
j=11; cout << "setting j to 11" << endl;
cout << "i=" << i << " j=" <<j<< " a.i1=" << a.geti() << endl;
return 0;
}
Program output:
before change:
&a.i1=0x7fff1b624140 &i=0x7fff1b624140 &j=0x7fff1b624150
i=5 j=10 a.i1=5
setting i to 6
i=6 j=10 a.i1=6
after change:
&a.i1=0x7fff1b624150 &i=0x7fff1b624140 &j=0x7fff1b624150
i=6 j=10 a.i1=10
setting j to 11
i=6 j=11 a.i1=11
As you can see that a.i1 initially points to i, after the change, it points to j.
However, doing so is considered as dangerous and thus unrecommended, because it defeats the original purpose of data encapsulation and OOP. It is more like memory address hacking.
You cannot reassign a reference.
That's not possible in the way you want. C++ just doesn't let you rebind what a reference points to.
However if you want to use trickery you can almost simulate it with a new scope (NEVER do this in a real program):
int a = 2;
int b = 4;
int &ref = a;
{
int& ref = b; // Shadows the original ref so everything inside this { } refers to `ref` as `b` now.
}
You can make a reference wrapper very easy using the placement new:
template< class T >
class RefWrapper
{
public:
RefWrapper( T& v ) : m_v( v ){}
operator T&(){ return m_v; }
T& operator=( const T& a ){ m_v = a; return m_v;}
//...... //
void remap( T& v )
{
//re-map reference
new (this) RefWrapper(v);
}
private:
T& m_v;
};
int32 a = 0;
int32 b = 0;
RefWrapper< int > r( a );
r = 1; // a = 1 now
r.remap( b );
r = 2; // b = 2 now
This is possible. Because under the hood, reference is a pointer. The following code will print "hello world"
#include "stdlib.h"
#include "stdio.h"
#include <string>
using namespace std;
class ReferenceChange
{
public:
size_t otherVariable;
string& ref;
ReferenceChange() : ref(*((string*)NULL)) {}
void setRef(string& str) {
*(&this->otherVariable + 1) = (size_t)&str;
}
};
void main()
{
string a("hello");
string b("world");
ReferenceChange rc;
rc.setRef(a);
printf("%s ", rc.ref.c_str());
rc.setRef(b);
printf("%s\n", rc.ref.c_str());
}
It is impossible, as other answers state.
However, if you store your reference in a class
or struct
, you could re-create the whole thing using placement new, so the reference is re-bound. As @HolyBlackCat noted, don't forget to use std::launder
to access the re-created object or use the pointer returned from the placement new. Consider my example:
#include <iostream>
struct A {
A(int& ref) : ref(ref) {}
// A reference stored as a field
int& ref;
};
int main() {
int a = 42;
int b = 43;
// When instance is created, the reference is bound to a
A ref_container(a);
std::cout <<
"&ref_container.ref = " << &ref_container.ref << std::endl <<
"&a = " << &a << std::endl << std::endl;
// Re-create the instance, and bind the reference to b
A* new_ref_container = new(&ref_container) A(b);
std::cout <<
// &ref_container and new_ref_container are the same pointers
"&ref_container = " << &ref_container << std::endl <<
"new_ref_container = " << new_ref_container << std::endl <<
"&new_ref_container.ref = " << &new_ref_container->ref << std::endl <<
"&b = " << &b << std::endl << std::endl;
return 0;
}
demo
The output is:
&ref_container.ref = 0x7ffdcb5f8c44
&a = 0x7ffdcb5f8c44
&ref_container = 0x7ffdcb5f8c38
new_ref_container = 0x7ffdcb5f8c38
&new_ref_container.ref = 0x7ffdcb5f8c40
&b = 0x7ffdcb5f8c40
Although its a bad idea as it defeats the purpose of using references, it is possible to change the reference directly
const_cast< int& >(ref)=b;
精彩评论