Aufrufen von C++-Memberfunktionen über einen Funktionszeiger

Lesezeit: 12 Minuten

Aufrufen von C Memberfunktionen uber einen Funktionszeiger
Toni das Pony

Wie erhalte ich einen Funktionszeiger für eine Klassenmitgliedsfunktion und rufe diese Mitgliedsfunktion später mit einem bestimmten Objekt auf? Ich möchte schreiben:

class Dog : Animal
{
    Dog ();
    void bark ();
}

…
Dog* pDog = new Dog ();
BarkFunction pBark = &Dog::bark;
(*pBark) (pDog);
…

Wenn möglich, möchte ich den Konstruktor auch über einen Zeiger aufrufen:

NewAnimalFunction pNew = &Dog::Dog;
Animal* pAnimal = (*pNew)();    

Ist dies möglich, und wenn ja, was ist der bevorzugte Weg, dies zu tun?

  • Ich verstehe immer noch nicht wirklich, warum, wenn Sie eine Member-Funktion eines Objekts aufrufen und dann einfach einen Zeiger auf das Objekt übergeben möchten? Wenn sich die Leute darüber beschweren, weil es Ihnen ermöglicht, die Klasse besser zu kapseln, warum nicht eine Schnittstellenklasse erstellen, von der alle Klassen erben?

    – Tschad

    28. September 2009 um 8:39 Uhr

  • Es kann bei der Implementierung von so etwas wie dem Befehlsmuster nützlich sein, obwohl viele Leute boost::function verwenden würden, um die Mechanik des rohen Member-Zeigers zu verbergen.

    – CB Bailey

    28. September 2009 um 8:45 Uhr

  • Warum weisen Sie diesen Hund dynamisch zu? Anschließend müssen Sie das Objekt ebenfalls manuell löschen. Das sieht sehr danach aus, als ob Sie von Java, C# oder einer anderen vergleichbaren Sprache kommen und immer noch mit C++ kämpfen. Ein einfaches automatisches Objekt (Dog dog;) ist eher das, was Sie wollen.

    – sbi

    28. September 2009 um 9:18 Uhr

  • @Chad: Ich würde meistens zustimmen, aber es gibt Zeiten, in denen das Übergeben einer Referenz teurer wäre. Stellen Sie sich eine Schleife vor, die über eine Art von Daten (Parsing, Berechnung usw.) iteriert, als dass das Aufrufen einer Funktion auf der Grundlage einiger Wenn/Sonst-Berechnungen Kosten verursacht, bei denen das Aufrufen der Pointed-to-Funktion ein solches Wenn/Dann vermeiden könnte /else prüft, ob diese Prüfungen vor Eintritt in die Schleife durchgeführt werden könnten.

    – Erich

    25. Oktober 2012 um 18:04 Uhr


  • Siehe auch Funktionszeiger auf Memberfunktion.

    – jww

    2. August 2019 um 4:54 Uhr

Aufrufen von C Memberfunktionen uber einen Funktionszeiger
Satbir

Lesen Das für Details:

// 1 define a function pointer and initialize to NULL

int (TMyClass::*pt2ConstMember)(float, char, char) const = NULL;

// C++

class TMyClass
{
public:
   int DoIt(float a, char b, char c){ cout << "TMyClass::DoIt"<< endl; return a+b+c;};
   int DoMore(float a, char b, char c) const
         { cout << "TMyClass::DoMore" << endl; return a-b+c; };

   /* more of TMyClass */
};
pt2ConstMember = &TMyClass::DoIt; // note: <pt2Member> may also legally point to &DoMore

// Calling Function using Function Pointer

(*this.*pt2ConstMember)(12, 'a', 'b');

  • Überraschend, dass sie entschieden haben, dass dies: *this.*pt2Member würde funktionieren. * hat Vorrang vor .*…hätte ich persönlich noch geschrieben this->*pt2Memberdas ist ein Operator weniger.

    – Alexis Wilke

    17. Februar 2014 um 3:51 Uhr

  • Warum muss man initialisieren pt2ConstMember zu NULL?

    – Ciro Santilli Путлер Капут 六四事

    1. Juli 2015 um 9:00 Uhr

  • @AlexisWilke warum ist es überraschend? Für direkte Objekte (nicht Zeiger) ist es das (object.*method_pointer)also wollen wir die * größere Priorität zu haben.

    – Ciro Santilli Путлер Капут 六四事

    1. Juli 2015 um 9:02 Uhr

  • @TomášZato, wenn ich mich nicht irre (und ich könnte mich irren), this wird nur verwendet, um zu demonstrieren, dass was auch immer Sie anwenden .* to sollte ein Zeiger auf eine Instanz der (Unter-)Klasse sein. Dies ist jedoch eine neue Syntax für mich. Ich vermute nur basierend auf anderen Antworten und Ressourcen, die hier verlinkt sind. Ich schlage eine Bearbeitung vor, um das klarer zu machen.

    – c1moore

    31. Januar 2018 um 23:15 Uhr

  • Warum rufen wir durch? *this? Ist das wirklich notwendig? Wie wäre es mit this->*pt2ConstMember)(12, 'a', 'b')? Würde das nicht funktionieren? Wie wäre es mit this->pt2ConstMember)(12, 'a', 'b')? Würde das nicht funktionieren? Siehe die Zusammenfassung der Antwort am Ende meiner Frage hier. Ich bin mir ziemlich sicher, die * vor dem pt2ConstMember Der Funktionszeiger ist optional, wenn er als Funktion aufgerufen wird.

    – Gabriel Staples

    5. Februar um 23:46 Uhr


Wie erhalte ich einen Funktionszeiger für eine Klassenmitgliedsfunktion und rufe diese Mitgliedsfunktion später mit einem bestimmten Objekt auf?

Am einfachsten ist es, mit a anzufangen typedef. Für eine Member-Funktion fügen Sie den Klassennamen in der Typdeklaration hinzu:

typedef void(Dog::*BarkFunction)(void);

Um dann die Methode aufzurufen, verwenden Sie die ->* Operator:

(pDog->*pBark)();

Außerdem möchte ich, wenn möglich, den Konstruktor auch über einen Zeiger aufrufen. Ist dies möglich, und wenn ja, was ist der bevorzugte Weg, dies zu tun?

Ich glaube nicht, dass Sie mit solchen Konstruktoren arbeiten können – ctors und dtors sind etwas Besonderes. Der normale Weg, um so etwas zu erreichen, wäre die Verwendung einer Factory-Methode, die im Grunde nur eine statische Funktion ist, die den Konstruktor für Sie aufruft. Ein Beispiel finden Sie im folgenden Code.

Ich habe Ihren Code geändert, um im Grunde das zu tun, was Sie beschreiben. Es gibt einige Vorbehalte unten.

#include <iostream>

class Animal
{
public:

    typedef Animal*(*NewAnimalFunction)(void);

    virtual void makeNoise()
    {
        std::cout << "M00f!" << std::endl;
    }
};

class Dog : public Animal
{
public:

    typedef void(Dog::*BarkFunction)(void);

    typedef Dog*(*NewDogFunction)(void);

    Dog () {}

    static Dog* newDog()
    {
        return new Dog;
    }

    virtual void makeNoise ()
    {
        std::cout << "Woof!" << std::endl;
    }
};

int main(int argc, char* argv[])
{
    // Call member function via method pointer
    Dog* pDog = new Dog ();
    Dog::BarkFunction pBark = &Dog::makeNoise;

    (pDog->*pBark)();

    // Construct instance via factory method
    Dog::NewDogFunction pNew = &Dog::newDog;

    Animal* pAnimal = (*pNew)();

    pAnimal->makeNoise();

    return 0;
}

Jetzt können Sie zwar normalerweise a verwenden Dog* an Stelle eines Animal* Dank der Magie des Polymorphismus tut es der Typ eines Funktionszeigers nicht Befolgen Sie die Suchregeln der Klassenhierarchie. Ein Animal-Methodenzeiger ist also nicht mit einem Dog-Methodenzeiger kompatibel, mit anderen Worten, Sie können keinen zuweisen Dog* (*)() zu einer Variablen vom Typ Animal* (*)().

Die Statik newDog -Methode ist ein einfaches Beispiel für eine Factory, die einfach neue Instanzen erstellt und zurückgibt. Da es sich um eine statische Funktion handelt, hat es eine reguläre typedef (ohne Klassenkennzeichen).

Nachdem ich das oben Gesagte beantwortet habe, frage ich mich, ob es nicht einen besseren Weg gibt, das zu erreichen, was Sie brauchen. Es gibt ein paar spezifische Szenarien, in denen Sie so etwas tun würden, aber Sie werden vielleicht feststellen, dass es andere Muster gibt, die für Ihr Problem besser funktionieren. Wenn Sie allgemeiner beschreiben, was Sie zu erreichen versuchen, kann sich der Hive-Mind als noch nützlicher erweisen!

Im Zusammenhang mit dem oben Gesagten werden Sie zweifellos die finden Bindung steigern Bibliothek und andere verwandte Module sehr nützlich.

  • Ich benutze C++ seit über 10 Jahren und lerne regelmäßig etwas Neues. Ich hatte noch nie davon gehört ->* vorher, aber jetzt hoffe ich, dass ich es nie brauchen werde 🙂

    – Thomas

    28. September 2009 um 11:37 Uhr

Ich glaube nicht, dass hier jemand erklärt hat, dass ein Problem darin besteht, dass Sie “Mitgliederzeiger” anstelle von normalen Funktionszeigern.

Elementzeiger auf Funktionen sind nicht einfach Funktionszeiger. In Bezug auf die Implementierung kann der Compiler keine einfache Funktionsadresse verwenden, da Sie die aufzurufende Adresse im Allgemeinen nicht kennen, bis Sie wissen, für welches Objekt dereferenziert werden soll (denken Sie an virtuelle Funktionen). Sie müssen auch das Objekt kennen, um das bereitzustellen this impliziter Parameter natürlich.

Nachdem ich gesagt habe, dass Sie sie brauchen, sage ich jetzt, dass Sie sie wirklich vermeiden müssen. Im Ernst, Mitgliederzeiger sind ein Schmerz. Es ist viel vernünftiger, sich objektorientierte Entwurfsmuster anzusehen, die dasselbe Ziel erreichen, oder eine zu verwenden boost::function oder was auch immer wie oben erwähnt – vorausgesetzt, Sie können diese Wahl treffen.

Wenn Sie diesen Funktionszeiger an vorhandenen Code liefern, dann wirklich brauchen B. ein einfacher Funktionszeiger, sollten Sie eine Funktion als statisches Mitglied der Klasse schreiben. Eine statische Member-Funktion versteht das nicht this, also müssen Sie das Objekt als expliziten Parameter übergeben. Es gab einmal eine nicht ganz so ungewöhnliche Redewendung in dieser Richtung für die Arbeit mit altem C-Code, der Funktionszeiger benötigt

class myclass
{
  public:
    virtual void myrealmethod () = 0;

    static void myfunction (myclass *p);
}

void myclass::myfunction (myclass *p)
{
  p->myrealmethod ();
}

Seit myfunction wirklich nur eine normale Funktion ist (Bereichsprobleme beiseite), kann ein Funktionszeiger auf die normale C-Weise gefunden werden.

BEARBEITEN – Diese Art von Methode wird “Klassenmethode” oder “statische Elementfunktion” genannt. Der Hauptunterschied zu einer Nicht-Member-Funktion besteht darin, dass Sie, wenn Sie von außerhalb der Klasse darauf verweisen, den Gültigkeitsbereich mithilfe von angeben müssen :: Oszilloskopauflösungsoperator. Um beispielsweise den Funktionszeiger zu erhalten, verwenden Sie &myclass::myfunction und um es Gebrauch zu nennen myclass::myfunction (arg);.

So etwas ist ziemlich üblich, wenn die alten Win32-APIs verwendet werden, die ursprünglich eher für C als für C++ entwickelt wurden. In diesem Fall ist der Parameter natürlich eher LPARAM oder ähnliches als ein Zeiger, und es ist etwas Casting erforderlich.

  • ‘myfunction’ ist keine normale Funktion, wenn Sie mit normal eine Funktion im C-Stil meinen. ‘myfunction’ wird genauer eine Methode von myclass genannt. Methoden einer Klasse sind nicht wie normale Funktionen, da sie etwas haben, was eine Funktion im C-Stil nicht hat, nämlich den ‘this’-Zeiger.

    – Erich

    25. Oktober 2012 um 17:57 Uhr

  • Die Empfehlung, Boost zu verwenden, ist drakonisch. Es gibt praktische gute Gründe für die Verwendung von Methodenzeigern. Es macht mir nichts aus, Boost als Alternative zu erwähnen, aber ich hasse es, wenn jemand sagt, jemand anderes sollte es verwenden, ohne alle Fakten zu kennen. Boost hat seinen Preis! Und wenn es sich um eine eingebettete Plattform handelt, ist dies möglicherweise keine mögliche Wahl. Abgesehen davon gefällt mir dein Beitrag sehr gut.

    – Erich

    25. Oktober 2012 um 18:10 Uhr

  • @Eric – Zu deinem zweiten Punkt wollte ich nicht sagen “Du sollst Boost verwenden”, und tatsächlich habe ich Boost selbst nie verwendet. Die Absicht (soweit ich es nach 3 Jahren weiß) war, dass die Leute nach Alternativen suchen sollten, und ein paar Möglichkeiten aufzuzählen. “Oder was auch immer” zeigt an, dass eine Liste nicht erschöpfend sein soll. Member-Zeiger haben einen Preis für die Lesbarkeit. Ihre prägnante Quellendarstellung kann auch Laufzeitkosten verschleiern – insbesondere muss ein Member-Zeiger auf eine Methode sowohl mit nicht-virtuellen als auch mit virtuellen Methoden zurechtkommen und muss wissen, welche.

    Benutzer180247

    25. Oktober 2012 um 22:09 Uhr

  • @Eric – Nicht nur das, diese Probleme sind ein Grund für die Nichtportabilität mit Member-Zeigern – Visual C++ benötigte zumindest in der Vergangenheit einige zusätzliche Hinweise zur Darstellung von Member-Zeigertypen. Ich würde den statischen Funktionsansatz für ein eingebettetes System verwenden – die Darstellung eines Zeigers ist die gleiche wie bei jedem anderen Funktionszeiger, die Kosten sind offensichtlich und es gibt kein Portabilitätsproblem. Und der von der statischen Memberfunktion umschlossene Aufruf weiß (zur Kompilierzeit), ob der Aufruf virtuell ist oder nicht – es sind keine Laufzeitprüfungen erforderlich, die über die üblichen Vtable-Lookups für virtuelle Methoden hinausgehen.

    Benutzer180247

    25. Oktober 2012 um 22:15 Uhr


  • @Eric – zu Ihrem ersten Punkt – mir ist bewusst, dass eine statische Member-Funktion nicht genau dasselbe ist wie eine Funktion im C-Stil (daher “Bereichsprobleme beiseite”), aber ich hätte wahrscheinlich den Namen einfügen sollen.

    Benutzer180247

    25. Oktober 2012 um 22:23 Uhr

1647131413 169 Aufrufen von C Memberfunktionen uber einen Funktionszeiger
Khaled Alshaya

typedef void (Dog::*memfun)();
memfun doSomething = &Dog::bark;
....
(pDog->*doSomething)(); // if pDog is a pointer
// (pDog.*doSomething)(); // if pDog is a reference

Aufrufen von C Memberfunktionen uber einen Funktionszeiger
Ciro Santilli Путлер Капут 六四事

Minimales lauffähiges Beispiel

main.cpp

#include <cassert>

class C {
    public:
        int i;
        C(int i) : i(i) {}
        int m(int j) { return this->i + j; }
};

int main() {
    // Get a method pointer.
    int (C::*p)(int) = &C::m;

    // Create a test object.
    C c(1);
    C *cp = &c;

    // Operator .*
    assert((c.*p)(2) == 3);

    // Operator ->*
    assert((cp->*p)(2) == 3);
}

Kompilieren und ausführen:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

Getestet unter Ubuntu 18.04.

Sie können die Reihenfolge der Klammern nicht ändern oder weglassen. Folgendes funktioniert nicht:

c.*p(2)
c.*(p)(2)

GCC 9.2 würde fehlschlagen mit:

main.cpp: In function ‘int main()’:
main.cpp:19:18: error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘p (...)’, e.g. ‘(... ->* p) (...)’
   19 |     assert(c.*p(2) == 3);
      |

C++11-Standard

.* und ->* Bereich Einzeloperatoren in C++ zu diesem Zweck eingeführt und in C nicht vorhanden.

C++11 N3337-Standardentwurf:

  • 2.13 “Operatoren und Satzzeichen” hat eine Liste aller Operatoren, die enthält .* und ->*.
  • 5.5 „Pointer-to-Member-Operatoren“ erklärt, was sie tun

Ich bin hierher gekommen, um zu lernen, wie man einen Funktionszeiger (keinen Methodenzeiger) aus einer Methode erstellt, aber keine der Antworten hier bietet eine Lösung. Hier ist, was ich herausgefunden habe:

template <class T> struct MethodHelper;
template <class C, class Ret, class... Args> struct MethodHelper<Ret (C::*)(Args...)> {
    using T = Ret (C::*)(Args...);
    template <T m> static Ret call(C* object, Args... args) {
        return (object->*m)(args...);
    }
};

#define METHOD_FP(m) MethodHelper<decltype(m)>::call<m>

Also für Ihr Beispiel würden Sie jetzt tun:

Dog dog;
using BarkFunction = void (*)(Dog*);
BarkFunction bark = METHOD_FP(&Dog::bark);
(*bark)(&dog); // or simply bark(&dog)

Bearbeiten:

Mit C++17 gibt es eine noch bessere Lösung:

template <auto m> struct MethodHelper;
template <class C, class Ret, class... Args, Ret (C::*m)(Args...)> struct MethodHelper<m> {
    static Ret call(C* object, Args... args) {
        return (object->*m)(args...);
    }
};

die direkt ohne das Makro verwendet werden kann:

Dog dog;
using BarkFunction = void (*)(Dog*);
BarkFunction bark = MethodHelper<&Dog::bark>::call;
(*bark)(&dog); // or simply bark(&dog)

Für Methoden mit Modifikatoren wie const Möglicherweise benötigen Sie weitere Spezialisierungen wie:

template <class C, class Ret, class... Args, Ret (C::*m)(Args...) const> struct MethodHelper<m> {
    static Ret call(const C* object, Args... args) {
        return (object->*m)(args...);
    }
};

1647131414 47 Aufrufen von C Memberfunktionen uber einen Funktionszeiger
Benjamin

Ein Funktionszeiger auf ein Klassenmitglied ist ein Problem, das sich sehr gut für die Verwendung von boost::function eignet. Kleines Beispiel:

#include <boost/function.hpp>
#include <iostream>

class Dog 
{
public:
   Dog (int i) : tmp(i) {}
   void bark ()
   {
      std::cout << "woof: " << tmp << std::endl;
   }
private:
   int tmp;
};



int main()
{
   Dog* pDog1 = new Dog (1);
   Dog* pDog2 = new Dog (2);

   //BarkFunction pBark = &Dog::bark;
   boost::function<void (Dog*)> f1 = &Dog::bark;

   f1(pDog1);
   f1(pDog2);
}

995470cookie-checkAufrufen von C++-Memberfunktionen über einen Funktionszeiger

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

Privacy policy