Wie kann ich die Variable ändern, auf die sich eine C ++ - Referenz bezieht?

75

Wenn ich das habe:

int a = 2;
int b = 4;
int &ref = a;

Wie kann ich nach diesem Code darauf refverweisen b?

Taru
quelle

Antworten:

97

Dies ist nicht möglich, und das ist beabsichtigt . Referenzen können nicht zurückgebunden werden.

Björn Pollex
quelle
Im folgenden Code ändere ich die Referenz von x in k und erhalte immer noch das Ergebnis. Können Sie mir sagen, wie das möglich ist? int a = 50; int & x = a; int k = 10; cout << x << endl; x = k; cout << x << endl;
Rajesh
4
Sie ändern die Referenz nicht, Sie weisen kdem Objekt, auf das verwiesen wird, den Wert von zu x. Nach dieser Zuordnung a == kund xbezieht sich immer noch auf a.
Björn Pollex
80

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.

David C. Bishop
quelle
20

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
John Humphreys - w00te
quelle
4
Sie erklären neu ptr.
Beta Carotin
8

Sie können eine Referenz nicht neu zuweisen.

Griwes
quelle
8

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.

xuancong84
quelle
Das ist nicht richtig: Eine Referenz muss überhaupt kein Zeiger / Objekt sein. Gemäß dem Standard muss es sich nur wie eine andere Beschriftung für dasselbe Objekt verhalten. Der Versuch, dieses undefinierte Verhalten zu ändern. Wenn Ihr Compiler in Ihrem spezifischen Programm beschließt, es über Zeiger zu implementieren, können Sie es möglicherweise ändern, aber das kann auch jederzeit unterbrochen werden.
ABaumstumpf
Deshalb erwähne ich im 1. Satz: "Formal gesehen ist das unmöglich, da es vom Design her verboten ist." Ich habe gerade eine Problemumgehung auf Implementierungsebene bereitgestellt.
xuancong84
5

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.
}
Mark B.
quelle
3

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
Schmecker
quelle
3
Diese Antwort ist viel komplizierter als nötig - die Frage war sehr einfach und konnte mit einem Einzeiler beantwortet werden.
Lukelazarovic
Wenn ich mich richtig erinnere, ist dies vor dem C ++ 20 wegen des Fehlens von schlecht geformt std::launder. Sie hätten einfach einen Zeiger verwenden können.
HolyBlackCat
3

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());
}
Zhaolin Feng
quelle
Welchen Effekt Sie auch immer auf UB haben, bedeutet nicht, dass es "funktioniert"
Slava
@Slava: Ist es tatsächlich undefiniertes Verhalten? Ist eine Referenz nicht sozusagen als "verkleideter Zeiger" definiert?
Einpoklum
otherVariableist nicht erforderlich mit *((uintptr_t*)this) = ((uintptr_t)&str)insetRef
joas
0

Es ist unmöglich, wie andere Antworten besagen.

Wenn Sie Ihre Referenz jedoch in einem classoder 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::launderauf 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;
}

demo

Die Ausgabe ist:

&ref_container.ref = 0x7ffdcb5f8c44
&a = 0x7ffdcb5f8c44

&ref_container = 0x7ffdcb5f8c38
new_ref_container = 0x7ffdcb5f8c38
&new_ref_container.ref = 0x7ffdcb5f8c40
&b = 0x7ffdcb5f8c40
ivaigult
quelle
2
Wenn ich mich richtig erinnere, verursacht dies UB vor C ++ 20, weil Sie es vergessen haben std::launder.
HolyBlackCat
@HolyBlackCat Ja, ich denke du hast recht. Der relevante Abschnitt ist basic.life . Ich kann über den alten Zeiger / die alte Variable nicht auf das neue Objekt zugreifen.
ivaigult
-5

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;
visu
quelle
9
Es ist lustig, wie Leute +1 falsche Lösung. Dieser Code ändert die Variable, auf die ref verweist, und verweist nicht auf sich selbst
Slava
Auf Exchange-Websites überrascht mich nichts mehr: D
CoffeDeveloper
1
Hey, das ist eindeutig eine falsche Antwort. @visu, bitte entfernen Sie es.
Einpoklum