Frage

In C ++ kann Mitglied Funktionszeiger verwendet werden, um abgeleitet zu Punkt (oder auch Base) die Teilnehmer?

EDIT: Vielleicht wird ein Beispiel helfen. Angenommen, wir eine Hierarchie von drei Klassen X haben, Y, Z um die Vererbung. Y hat daher eine Basisklasse X und eine abgeleitete Klasse Z.

Jetzt können wir ein Mitglied Funktionszeiger p für Klasse Y definieren. Dies wird geschrieben als:

void (Y::*p)();

(Der Einfachheit halber werde ich übernehmen wir in Funktionen mit der Unterschrift void f() nur interessiert sind)

Dieser Zeiger p nun verwendet werden, um Elementfunktionen der Klasse Y zu zeigen.

Diese Frage (zwei Fragen, wirklich) ist dann:

  1. Kann auf eine Funktion in der abgeleiteten Klasse p Punkt Z verwendet werden?
  2. Kann auf eine Funktion in der Basisklasse p Punkt X verwendet werden?
War es hilfreich?

Lösung

C ++ 03 std, §4.11 2 Zeiger auf Mitglied Conversions :

  

rvalue der Typs „Zeiger auf Glied von B vom Typ cv T“, wobei B ein Klasse-Typ ist, kann zu einem R-Wert des Typs „Zeiger auf Mitglied D vom Typ umgewandelt werden < i> cv T“, wobei D eine abgeleitete Klasse (Ziffer 10) von B sind, wenn B ein unzugänglicher (Abschnitt 11), mehrdeutig (10.2) oder virtuell (10.1) die Basisklasse von D ist, ein Programm, das diese Umwandlung erfordert wird schlecht gebildet. Das Ergebnis der Umwandlung bezieht sich auf das gleiche Element als Zeiger auf ein Element, bevor die Umwandlung stattgefunden hat, aber es bezieht sich auf die Mitgliedsbasisklasse, als ob es ein Mitglied der abgeleiteten Klasse war. Das Ergebnis bezieht sich auf das Element in D's Instanz von B. Da das Ergebnis hat Typen „Zeiger auf ein Element von D vom Typ cv T“, es kann mit einem D-Objekt sein dereferenzieren. Das Ergebnis ist das gleiche, als wenn der Zeiger auf Glied von B mit dem B-Unterobjekt von D. Der Nullelement Zeigerwert dereferenziert wurde den Nullelement Zeigerwert des Zieltypen umgewandelt wird. 52)

     

52) Die Regel für die Umwandlung von Zeigern auf Mitglieder (von Zeigern auf ein Element von Base zu Mitglied abgeleiteter Zeiger) im Vergleich zum Zeiger auf Objekte von der Regel invertiert angezeigt wird (von Zeigern auf abgeleitet Zeiger auf Base) (4,10, Abschnitt 10). Diese Umkehrung ist notwendig, Typsicherheit zu gewährleisten. Beachten Sie, dass ein Zeiger auf ein Element nicht ein Zeiger auf Objekt oder ein Zeiger auf Funktion und die Regeln für die Umwandlungen solcher Zeiger gelten nicht für Zeiger auf Elemente. Insbesondere wird ein Zeiger auf ein Element kann nicht in einen Hohlraum umgewandelt werden *.

Kurz gesagt, können Sie einen Zeiger auf ein Element eine barrierefreien, nicht-virtuellen Basisklasse auf einen Zeiger auf ein Mitglied einer abgeleiteten Klasse umwandeln, solange das Mitglied nicht eindeutig ist.

class A {
public: 
    void foo();
};
class B : public A {};
class C {
public:
    void bar();
};
class D {
public:
    void baz();
};
class E : public A, public B, private C, public virtual D {
public: 
    typedef void (E::*member)();
};
class F:public E {
public:
    void bam();
};
...
int main() {
   E::member mbr;
   mbr = &A::foo; // invalid: ambiguous; E's A or B's A?
   mbr = &C::bar; // invalid: C is private 
   mbr = &D::baz; // invalid: D is virtual
   mbr = &F::bam; // invalid: conversion isn't defined by the standard
   ...

Umwandlung in der anderen Richtung (über static_cast) von § 5.2.9 9:

  

rvalue der Typs "Zeiger auf Mitglied D vom Typ CV1 T" kann zu einem R-Wert des Typs „Zeiger auf ein Element von B vom Typ umgewandelt werden, CV2 T "wobei B eine Basisklasse (Klausel 10 class.derived ) D, wenn eine gültige Standard-Konvertierung von‚Zeigern auf Glied von B vom Typ T‘auf‚Zeiger auf ein Element von D vom Typ T‘vorhanden ist ( 4.11 conv.mem ) und CV2 die gleiche cv-Qualifikation als oder größer cv-Qualifikation als, CV1 . 11) das Null Mitglied Zeigerwert ( 4.11 conv.mem ) mit dem null-Elemente Zeigerwert des Zieltypen umgewandelt. Wenn Klasse B enthält das ursprüngliche Element oder eine Base ist oder abgeleiteten Klasse der Klasse des ursprünglichen Element enthält, das sich ergebende Zeiger auf Elementpunkte zu dem ursprünglichen Element. Andernfalls ist das Ergebnis der Besetzung nicht definiert. [Anmerkung: Obwohl Klasse B nicht das ursprüngliche Element, die dynamische Art des Objekts, auf das der Zeiger auf ein Element dereferenziert enthalten muß das ursprüngliche Element enthalten muß; finden Sie unter 5.5 expr.mptr.oper .]

     

11) Typen Funktion (einschließlich der in Zeiger auf Member-Funktion verwendet     Typen) sind nie cv-qualifiziert; finden Sie unter 8.3. 5 dcl.fct .

Kurz gesagt, können Sie aus einer abgeleiteten konvertierenD::* zu einer Basis B::*, wenn Sie von einem B::* zu einem D::* umwandeln können, wenn man nur die B::* auf Objekte verwenden kann, die vom Typ D sind oder von D abstammen.

Andere Tipps

Ich bin nicht 100% sicher, was Sie fordern, aber hier ist ein Beispiel, das mit virtuellen Funktionen funktioniert:

#include <iostream>
using namespace std;

class A { 
public:
    virtual void foo() { cout << "A::foo\n"; }
};
class B : public A {
public:
    virtual void foo() { cout << "B::foo\n"; }
};

int main()
{
    void (A::*bar)() = &A::foo;
    (A().*bar)();
    (B().*bar)();
    return 0;
}

Der kritische Punkt mit Zeigern auf Mitglieder ist, dass sie zu jedem Verweis oder Zeiger auf eine Klasse des richtigen Typs angewendet werden kann. Das bedeutet, dass, weil Z von Y abgeleitet wird ein Zeiger (oder Referenz) vom Typ Zeiger (oder Referenz-) Y kann Punkt tatsächlich (oder lesen) auf die Basisklasse Subobjekt von Z oder andere Klasse von Y abgeleitet.

void (Y::*p)() = &Z::z_fn; // illegal

Das bedeutet, dass auf einen Zeiger auf Mitglied von Y zugewiesen alles muss mit jedem Y tatsächlich funktioniert. Wenn es erlaubt war an ein Mitglied der Z-zu-Punkt (das war kein Mitglied der Y), dann wäre es möglich, eine Member-Funktion von Z auf irgendeine Sache zu nennen, die nicht wirklich ein Z war.

Auf der anderen Seite, jeder Zeiger auf ein Element von Y weist auch das Mitglied von Z (Vererbung bedeutet, dass Z hat alle Attribute und Methoden ihrer Basis) ist es legal ist, einen Zeiger auf Mitglied von Y auf einen Zeiger zu konvertieren an das Mitglied des Z. Dies ist an sich sicher.

void (Y::*p)() = &Y::y_fn;
void (Z::*q)() = p; // legal and safe

Sie können diese Artikel überprüfen, Mitglied Funktionszeiger und die schnellstmögliche C ++ Delegierten Die kurze Antwort scheint in einigen Fällen ja, zu sein.

Ich glaube schon. Da die Funktionszeiger die Signatur selbst zu identifizieren verwendet, würde die Basis / abgeleiteten Verhalten verlassen, was auch immer Objekt, das Sie es aufgerufen.

Meine Experimente ergaben folgende: Achtung - dieses undefiniertes Verhalten sein könnte. Es wäre hilfreich, wenn jemand eine endgültige Referenz liefern könnte.

  1. Das funktionierte, aber erforderlich, um eine Besetzung, wenn die abgeleitete Elementfunktion zuweist p.
  2. Dies auch gearbeitet, aber erforderlich zusätzliche Abgüsse wenn p dereferencing.

Wenn wir wirklich ehrgeizig fühlen könnten wir fragen, ob p verwendet werden kann, um Elementfunktionen von nicht verwandten Klassen zu verweisen. Ich habe es nicht ausprobiert, aber die FastDelegate Seite in dagorym Antwort verknüpft schlägt es ist möglich.

Abschließend werde ich versuchen, auf diese Weise unter Verwendung Mitglied Funktionszeiger zu vermeiden. Passages wie die folgenden nicht schaffen Vertrauen:

  

Gießen zwischen Memberfunktion   Zeiger ist ein extrem trüber Bereich.   Während die Standardisierung von C ++,   viele Diskussionen gab es über   ob Sie ein in der Lage zu werfen sollten   Mitgliedsfunktionszeiger von einer Klasse   einem Mitglied Funktionszeiger einer Base   oder abgeleiteten Klasse, und ob Sie   Guss zwischen nicht verwandten Klassen konnte.   Durch die Zeit, der Normenausschuss   aus ihrem Geist, verschiedene Compiler   Anbieter hatte bereits   Umsetzung Entscheidungen, die hatten   sie in verschiedene Antworten gesperrt   diese Fragen. [ FastDelegate Artikel ]

Nehmen wir an, wir haben class X, class Y : public X, and class Z : public Y

Sie sollten in der Lage sein, Methoden für beide X zuweisen, Y auf Zeiger vom Typ void (Y :: * p) (), aber diese Methoden nicht für Z. Um zu sehen, warum sollten Sie Folgendes berücksichtigen:

void (Y::*p)() = &Z::func; // we pretend this is legal
Y * y = new Y; // clearly legal
(y->*p)(); // okay, follows the rules, but what would this mean?

Durch diese Zuordnung ermöglicht erlauben wir den Aufruf einer Methode für Z auf einem Y-Objekt, das zu wer weiß, was führen könnte. Sie können es alle Arbeiten machen, indem die Zeiger Gießen, aber das ist nicht sicher oder garantiert arbeiten.

Hier ist ein Beispiel dafür, was funktioniert. Sie können eine Methode in abgeleiteten Klasse überschreiben, und eine andere Methode der Basisklasse, die Zeiger auf diese überschriebene Methode verwendet in der Tat ruft die Methode der abgeleiteten Klasse.

#include <iostream>
#include <string>

using namespace std;

class A {
public:
    virtual void traverse(string arg) {
        find(&A::visit, arg);
    }

protected:
    virtual void find(void (A::*method)(string arg),  string arg) {
        (this->*method)(arg);
    }

    virtual void visit(string arg) {
        cout << "A::visit, arg:" << arg << endl;
    }
};

class B : public A {
protected:
    virtual void visit(string arg) {
        cout << "B::visit, arg:" << arg << endl;
    }
};

int main()
{
    A a;
    B b;
    a.traverse("one");
    b.traverse("two");
    return 0;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top