Wenn ich das habe:
int a = 2;
int b = 4;
int &ref = a;
Wie kann ich nach diesem Code darauf ref
verweisen b
?
Antworten:
Dies ist nicht möglich, und das ist beabsichtigt . Referenzen können nicht zurückgebunden werden.
k
dem Objekt, auf das verwiesen wird, den Wert von zu x
. Nach dieser Zuordnung a == k
und x
bezieht sich immer noch auf a
.
In C ++ 11 gibt es den neuen (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);
}
Dies ist auch nützlich, um Referenzen in Containern zu speichern.
Sie können eine Referenz nicht neu zuweisen, aber wenn Sie nach etwas suchen, das ähnliche Fähigkeiten bietet, können Sie stattdessen einen Zeiger verwenden.
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.
Sie können den Wert innerhalb des Zeigers abrufen oder festlegen mit:
*ptr = 5; //set
int c = *ptr; //get
ptr
.
Formal ist das unmöglich, da es vom Design her verboten ist. Das ist willkürlich möglich.
Eine Referenz wird als Zeiger gespeichert, sodass Sie jederzeit ändern können, wohin sie verweist, solange Sie wissen, wie Sie ihre Adresse erhalten. In ähnlicher Weise können Sie auch den Wert von const-Variablen, const-Mitgliedsvariablen oder sogar privaten Mitgliedsvariablen ändern, wenn Sie keinen Zugriff darauf haben.
Der folgende Code hat beispielsweise die Konstante für private Mitglieder der Klasse A geändert:
#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;
}
Programmausgabe:
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
Wie Sie sehen können, zeigt a.i1 zunächst auf i , nach der Änderung auf j .
Dies wird jedoch als gefährlich und daher nicht empfohlen angesehen, da es den ursprünglichen Zweck der Datenkapselung und des OOP zunichte macht. Es ist eher wie das Hacken von Speicheradressen.
Das ist nicht so möglich, wie Sie es wollen. In C ++ können Sie einfach nicht erneut binden, auf was eine Referenz verweist.
Wenn Sie jedoch Tricks anwenden möchten, können Sie diese fast mit einem neuen Bereich simulieren ( tun Sie dies NIEMALS in einem echten Programm):
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.
}
Mit der neuen Platzierung können Sie einen Referenz-Wrapper sehr einfach gestalten:
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
std::launder
. Sie hätten einfach einen Zeiger verwenden können.
Das ist möglich. Denn unter der Haube ist Referenz ein Zeiger. Der folgende Code gibt "Hallo Welt" aus.
#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());
}
otherVariable
ist nicht erforderlich mit *((uintptr_t*)this) = ((uintptr_t)&str)
insetRef
Es ist unmöglich, wie andere Antworten besagen.
Wenn Sie Ihre Referenz jedoch in einem class
oder speichern struct
, können Sie das Ganze mithilfe der Platzierung neu neu erstellen, sodass die Referenz erneut gebunden wird. Vergessen Sie nicht, mit @HolyBlackCat std::launder
auf das neu erstellte Objekt zuzugreifen, oder verwenden Sie den Zeiger, der von der neuen Platzierung zurückgegeben wurde. Betrachten Sie mein Beispiel:
#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;
}
Die Ausgabe ist:
&ref_container.ref = 0x7ffdcb5f8c44
&a = 0x7ffdcb5f8c44
&ref_container = 0x7ffdcb5f8c38
new_ref_container = 0x7ffdcb5f8c38
&new_ref_container.ref = 0x7ffdcb5f8c40
&b = 0x7ffdcb5f8c40
std::launder
.
Obwohl es eine schlechte Idee ist, da es den Zweck der Verwendung von Referenzen zunichte macht, ist es möglich, die Referenz direkt zu ändern
const_cast< int& >(ref)=b;