Was sind die Pointer-to-Member-Operatoren ->* und .* in C++?

Lesezeit: 8 Minuten

Benutzer-Avatar
Benutzer541686

Ja, ich habe diese Frage gesehen und diese häufig gestellten Fragenaber ich still verstehe nicht was ->* und .* in C++ bedeuten.
Diese Seiten bieten Informationen Über die Operatoren (z. B. Überladen), scheinen aber nicht gut zu erklären, was sie sind sind.

Was sind ->* und .* in C++, und wann müssen Sie sie im Vergleich zu verwenden -> und .?

Benutzer-Avatar
Armen Tsirunjan

Ich hoffe, dieses Beispiel wird die Dinge für Sie klären

//we have a class
struct X
{
   void f() {}
   void g() {}
};

typedef void (X::*pointer)();
//ok, let's take a pointer and assign f to it.
pointer somePointer = &X::f;
//now I want to call somePointer. But for that, I need an object
X x;
//now I call the member function on x like this
(x.*somePointer)(); //will call x.f()
//now, suppose x is not an object but a pointer to object
X* px = new X;
//I want to call the memfun pointer on px. I use ->*
(px ->* somePointer)(); //will call px->f();

Jetzt können Sie nicht verwenden x.somePointer()oder px->somePointer() weil es in Klasse X kein solches Mitglied gibt. Dafür wird die spezielle Memberfunktionszeigeraufrufsyntax verwendet … probieren Sie einfach selbst ein paar Beispiele aus, Sie werden sich daran gewöhnen

  • Ergänzen Sie die erforderlich zusätzliche geschweifte Klammern, um den Vorrang von Operatoren zu kompensieren.

    – Martin York

    5. Juli 2011 um 17:33 Uhr

  • Eine Pointer-to-Member-Funktion kann größer als ein normaler Zeiger auf eine Funktion sein: speziell um mit der Vererbung fertig zu werden iirc

    – Nicht zu gebrauchen

    5. Juli 2011 um 18:05 Uhr

  • Stellen Sie es sich als “relativen” Zeiger vor: relativ zu einem Objekt – das Sie dann angeben müssen, um das endgültige Ziel tatsächlich zu erreichen.

    – Gr.

    9. August 2019 um 18:16 Uhr


  • Es ist wirklich eine gute Erklärung und was sind sie. Aber ich weiß nicht wirklich, warum und wann wir so etwas brauchen würden.

    – Zufällig

    5. Januar um 23:15 Uhr

Benutzer-Avatar
Spraff

EDIT: Übrigens wird es komisch für Zeiger für virtuelle Elementfunktionen.

Für Member-Variablen:

struct Foo {
   int a;
   int b;
};


int main ()
{
    Foo foo;
    int (Foo :: * ptr);

    ptr = & Foo :: a;
    foo .*ptr = 123; // foo.a = 123;

    ptr = & Foo :: b;
    foo .*ptr = 234; // foo.b = 234;
}

Die Mitgliederfunktionen sind fast gleich.

struct Foo {
   int a ();
   int b ();
};


int main ()
{
    Foo foo;
    int (Foo :: * ptr) ();

    ptr = & Foo :: a;
    (foo .*ptr) (); // foo.a ();

    ptr = & Foo :: b;
    (foo .*ptr) (); // foo.b ();
}

  • +1, um zu zeigen, dass die Syntax für alle Mitglieder gilt, nicht nur für Mitgliederfunktionen. Ich finde jedoch, dass Zeiger auf Member-Variablen sehr selten verwendet werden, trotz ihrer vielen ziemlich interessanten potenziellen Anwendungen.

    – Jon Purdy

    5. Juli 2011 um 17:18 Uhr

Kurz gesagt: Sie verwenden -> und . wenn Sie wissen, auf welches Mitglied Sie zugreifen möchten. Und Sie verwenden ->* und .* wenn du nicht wissen, auf welches Mitglied Sie zugreifen möchten.

Beispiel mit einer einfachen aufdringlichen Liste

template<typename ItemType>
struct List {
  List(ItemType *head, ItemType * ItemType::*nextMemPointer)
  :m_head(head), m_nextMemPointer(nextMemPointer) { }

  void addHead(ItemType *item) {
    (item ->* m_nextMemPointer) = m_head;
    m_head = item;
  }

private:
  ItemType *m_head;

  // this stores the member pointer denoting the 
  // "next" pointer of an item
  ItemType * ItemType::*m_nextMemPointer;
};

  • +1 für den ersten Satz, obwohl ich das noch nie in meinem Leben getan habe nicht bekannt, auf welches Mitglied ich zugreifen möchte, haha. 🙂

    – Benutzer541686

    5. Juli 2011 um 17:45 Uhr

Wenn Sie einen normalen Zeiger (auf ein Objekt oder einen Basistyp) haben, würden Sie verwenden * um es zu dereferenzieren:

int a;
int* b = a;
*b = 5;     // we use *b to dereference b, to access the thing it points to

Konzeptionell machen wir dasselbe mit einem Member-Funktionszeiger:

class SomeClass
{
   public:  void func() {}
};

// typedefs make function pointers much easier.
// this is a pointer to a member function of SomeClass, which takes no parameters and returns void
typedef void (SomeClass::*memfunc)();

memfunc myPointer = &SomeClass::func;

SomeClass foo;

// to call func(), we could do:
foo.func();

// to call func() using our pointer, we need to dereference the pointer:
foo.*myPointer();
// this is conceptually just:    foo  .  *myPointer  ();


// likewise with a pointer to the object itself:
SomeClass* p = new SomeClass;

// normal call func()
p->func();

// calling func() by dereferencing our pointer:
p->*myPointer();
// this is conceptually just:    p  ->  *myPointer  ();

Ich hoffe, das hilft, das Konzept zu erklären. Wir dereferenzieren unseren Zeiger effektiv auf die Member-Funktion. Es ist ein wenig komplizierter als das – es ist kein absoluter Zeiger auf eine Funktion im Speicher, sondern nur ein Offset, auf das angewendet wird foo oder p Oben. Aber konzeptionell dereferenzieren wir es, ähnlich wie wir einen normalen Objektzeiger dereferenzieren würden.

Sogenannte “Zeiger” auf Mitglieder in C++ sind intern eher wie Offsets. Sie benötigen sowohl einen solchen Mitglieds-“Zeiger” als auch ein Objekt, um auf das Mitglied im Objekt zu verweisen. Aber “Member-Zeiger” werden mit Zeigersyntax verwendet, daher der Name.

Es gibt zwei Möglichkeiten, wie Sie ein Objekt zur Hand haben können: Sie haben eine Referenz auf das Objekt, oder Sie haben einen Zeiger auf das Objekt.

Verwenden Sie als Referenz .* um es mit einem Mitgliedszeiger zu kombinieren, und verwenden Sie für den Zeiger ->* um es mit einem Mitgliedszeiger zu kombinieren.

Verwenden Sie jedoch in der Regel keine Elementzeiger, wenn Sie dies vermeiden können.

Sie gehorchen ziemlich kontraintuitiven Regeln und ermöglichen es, sie zu umgehen protected Zugriff ohne explizites Casting, also versehentlich…

Prost & hth.,

  • +1 für die gute Erklärung ohne Code. 🙂 Frage: Warum können wir die Adresse der Funktion nicht einfach wie die einer normalen Funktion nehmen? Unterscheidet sich der Zeiger auf eine Member-Funktion von einem Zeiger auf eine andere Funktion? (zB ist es größer?)

    – Benutzer541686

    5. Juli 2011 um 17:44 Uhr

  • @Mehrdad: Wenn Sie einen Zeiger auf eine Memberfunktion haben könnten, die auf nicht virtuelle Memberfunktionen beschränkt war, könnte es sich tatsächlich nur um die Adresse handeln. Die Virtualität oder nicht ist jedoch nicht Teil des Member-Funktionszeigertyps. Daher muss seine Darstellung einige Informationen darüber enthalten, ob sich der aktuelle Wert auf eine virtuelle Funktion bezieht oder nicht, und falls virtuell, für eine vtable-basierte Implementierung Informationen, die einen Offset in der vtable der Klasse bestimmen, der der Zeigertyp zugeordnet ist.

    – Prost und hth. – Alf

    21. Oktober 2014 um 17:51 Uhr

Benutzer-Avatar
Katze plus plus

Sie können den Zeiger auf Mitglieder nicht als normale Zeiger dereferenzieren, da dies für Elementfunktionen erforderlich ist this Zeiger, und Sie müssen es irgendwie übergeben. Sie müssen also diese beiden Operatoren verwenden, mit Objekt auf der einen Seite und Zeiger auf der anderen, z (object.*ptr)().

Erwägen Sie die Verwendung function und bind (std:: oder boost::je nachdem, ob Sie stattdessen C++03 oder 0x schreiben).

  • +1 für die gute Erklärung ohne Code. 🙂 Frage: Warum können wir die Adresse der Funktion nicht einfach wie die einer normalen Funktion nehmen? Unterscheidet sich der Zeiger auf eine Member-Funktion von einem Zeiger auf eine andere Funktion? (zB ist es größer?)

    – Benutzer541686

    5. Juli 2011 um 17:44 Uhr

  • @Mehrdad: Wenn Sie einen Zeiger auf eine Memberfunktion haben könnten, die auf nicht virtuelle Memberfunktionen beschränkt war, könnte es sich tatsächlich nur um die Adresse handeln. Die Virtualität oder nicht ist jedoch nicht Teil des Member-Funktionszeigertyps. Daher muss seine Darstellung einige Informationen darüber enthalten, ob sich der aktuelle Wert auf eine virtuelle Funktion bezieht oder nicht, und falls virtuell, für eine vtable-basierte Implementierung Informationen, die einen Offset in der vtable der Klasse bestimmen, der der Zeigertyp zugeordnet ist.

    – Prost und hth. – Alf

    21. Oktober 2014 um 17:51 Uhr

Pointer-to-Member-Zugriffsoperatoren: .* und ->*

Die Pointer-to-Member-Zugriffsoperatoren, .* und ->*dienen zur Dereferenzierung a Zeiger auf Mitglied in Kombination mit einem Objekt und ein Zeiger auf Objekt, bzw. Diese Beschreibung gilt für beide Zeiger auf Datenelemente und Zeiger auf Elementfunktionen.

Betrachten Sie zum Beispiel die Klasse Foo:

struct Foo {
   int i;
   void f();
};

Wenn Sie einen Elementzeiger deklarieren, iPtrzu einem int Datenmitglied von Foo:

int Foo::* iPtr;

Sie können diesen Elementzeiger initialisieren iPtr damit es auf die zeigt Foo::i Mitglied:

iPtr = &Foo::i;

Um diesen Zeiger zu dereferenzieren, müssen Sie ihn in Verbindung mit a verwenden Foo Objekt.

Betrachten Sie nun das Objekt foo und der Zeiger auf Objekt fooPtr:

Foo foo;
Foo* fooPtr = &foo;

Dann können Sie dereferenzieren iPtr in Kombination mit foo oder fooPtr:

foo.*iPtr = 0;
fooPtr->*iPtr = 0;

Analog können Sie verwenden .* und ->* mit Zeiger auf Funktionsmitglieder. Beachten Sie jedoch, dass Sie sie in Klammern setzen müssen, da die Funktionsaufrufoperatordh, ()hat eine höhere Priorität als beide .* und ->*:

void (Foo::*memFuncPtr)() = &Foo::f;

(foo.*memFuncPtr)();
(fooPtr->*memFuncPtr)();

Abschließend: Sie benötigen ein Objekt, um einen Zeiger auf ein Mitglied zu dereferenzieren, und welches Sie auch verwenden .* oder ->* zum Dereferenzieren des Zeigers auf das Mitglied, hängt davon ab, ob dieses benötigte Objekt direkt oder über einen Objektzeiger bereitgestellt wird.

C++17 – Verwenden std::invoke() stattdessen

Die Verwendung beider Operatoren kann seit C++17 durch die ersetzt werden std::invoke Funktionsvorlage. std::invoke bietet eine einheitliche Methode zum Dereferenzieren von Member-Zeigern, unabhängig davon, ob Sie sie in Kombination mit einem verwenden Objekt oder ein Objektzeigerund auch unabhängig davon, ob die Zeiger auf Mitglied entspricht a Zeiger auf Datenelement oder Zeiger auf Member-Funktion:

// dereference a pointer to a data member
std::invoke(iPtr, foo) = 0;      // with an object
std::invoke(iPtr, fooPtr) = 0;   // with an object pointer

// dereference a pointer to a member function
std::invoke(memFuncPtr, foo);      // with an object
std::invoke(memFuncPtr, fooPtr);   // with an object pointer

Diese vereinheitlichte Syntax entspricht der gewöhnlichen Funktionsaufrufsyntax und kann es einfacher machen, generischen Code zu schreiben.

1013310cookie-checkWas sind die Pointer-to-Member-Operatoren ->* und .* in C++?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy