Anhang I

Memberzeiger

Während 'normale' Zeiger auf eine Variable, ein Objekt oder eine Funktion verweisen, verweisen Memberzeiger auf ein Member innerhalb der Klasse. Da aber ein Zugriff auf Member (fast) immer nur über ein Objekt erfolgen kann, benötigt ein Memberzeiger ein Objekt als Referenz.

Technisch gesehen enthält ein Memberzeiger den Offset eines Members innerhalb seiner Klasse. Die effektive Adresse für den Zugriff bzw. Aufruf wird durch Addition der Objektadresse und des Offsets im Memberzeiger gebildet.

Schnittstellenzeiger (Zeiger auf Methoden)

Beginnen wir mit Zeiger auf Methoden, dem sogenannten Schnittstellenzeiger. Nachfolgend die Ausgangsklasse für das Beispiel zur Anwendung eines Schnittstellenzeigers.

#include <print>
// Klassendefinition
class Any
{
    // beliebige Methode
    void Method();
};
// Die aufzurufende Methode
void Any::Method()
{
    std::println("Methode ausgefuehrt");
}

Die Klasse Any enthält eine Methode Method() die über einen Schnittstellenzeiger aufgerufen werden soll.

Zunächst gilt es, den Schnittstellenzeiger innerhalb der Klasse zu definieren.

RTYP (KLASSE::*PNAME)([PARAMETER]);

RTYP ist der Returntyp der über den Schnittstellenzeiger aufzurufenden Methode, KLASSE deren Klasse und PNAME der Name des Schnittstellenzeigers. Besitzt die aufzurufende Methode Parameter, so sind zumindest deren Datentypen PARAMETER in der Parameterklammer aufzuführen.

Eleganter geht es mithilfe des decltype() Spezifizierers, da hierbei die Angabe des Returntyps und der Parameter entfällt.

#include <print>

// Klassendefinition
class Any
{
    // beliebige Methode
    void Method();
    // Definition des Schnittstellenzeigers
    // Alternativ: void (Any::*pfnMem) ();
    decltype(Any::Method) pfnMem;
};
// Die aufzurufende Methode
void Any::Method()
{
    std::println("Methode ausgefuehrt");
}

Beachten Sie, dass bei Verwendung von decltype() vor dem Zeigernamen kein '*' steht. Außerdem muss die Zeigerdefinition in diesem Fall nach der Deklaration der Methode stehen.

Um einem Schnittstellenzeiger eine Methode zuzuweisen, ist der voll qualifizierte Name der Methode anzugeben, so wie im Beispiel in der Methode Init() angegeben.

Und da der Schnittstellenzeiger private ist, wird noch eine weitere Methode benötigt die den Schnittstellenzeiger ausliest, sodass darüber später Any::Method() aufgerufen werden kann.

Die vollständige Klasse Any sieht damit wie folgt aus:

#include <print>

// Klassendefinition
class Any
{
    // beliebige Methode
    void Method();
    // Definition des Schnittstellenzeigers
    //Alternativ: void (Any::*pfnMem) ();
    decltype(Any::Method) pfnMem;
    // Initialisiert Schnittstellenzeiger
    void Init()
    {
        pfnMem = Any::Method;
    }
    // Liefert Schnittstellenzeiger zurueck
    decltype(pfnMem) GetMemPtr()
    {
        return pfnMem;
    }
};
// Die aufzurufende Methode
void Any::Method()
{
    std::println("Methode ausgefuehrt");
}

Bleibt noch der Aufruf der Methode übrig, auf die der Schnittstellenzeiger verweist. Beim Aufruf über den Schnittstellenzeiger ist innerhalb einer Klammer das Objekt anzugeben, dessen Methode aufgerufen werden soll, gefolgt vom Operator .* und dem Namen des Schnittstellenzeigers.

#include <print>

// Klassendefinition
class Any
{
    // beliebige Methode
    void Method();
    // Definition des Schnittstellenzeigers
    //Alternativ: void (Any::*pfnMem) ();
    decltype(Any::Method) pfnMem;
public:
    // Initialisiert Schnittstellenzeiger
    void Init()
    {
        pfnMem = Any::Method;
    }
    // Liefert Schnittstellenzeiger zurueck
    decltype(pfnMem) GetMemPtr()
    {
        return pfnMem;
    }
};
// Die aufzurufende Methode
void Any::Method()
{
    std::println("Methode ausgefuehrt");
}

// Definition eines Any-Objekts
Any anyObj;

int main()
{
    // Schnittstellenzeiger initialsieren
    anyObj.Init();
    // und auslesen
    auto ptrMem = anyObj.GetMemPtr();
    // Nun die Methode ueber den Zeiger aufrufen
    (anyObj.*ptrMem)();
};

Methode ausgefuehrt

Soll die Methode über einen Objektzeiger aufgerufen werden, ist anstelle des Operators .* der Operator ->* zu verwenden.

Der Aufruf einer Methode über einen Schnittstellenzeiger ist sogar dann möglich, wenn die Methode in einer private-Sektion steht (siehe Beispiel). Ein direkter Aufruf von Method() im obigen Beispiel würde zu einem Fehler beim Coimpilieren führen!

Eigenschaftszeiger (Datenzeiger)

Die Definition eines Eigenschaftszeigers und der Zugriff auf die Eigenschaft über den Zeiger erfolgt analog zum Schnittstellenzeiger.

#include <print>

// Klassendefinition
class Any
{
    // beliebiges Datum
    int value = 1;
    // Definition des Eigenschaftszeigers
    int Any::*pData;
    //decltype(&Any::value) pData;
public:
    // Initialisiert Eigenschaftszeiger
    void Init()
    {
        pData = &Any::value;
    }
    // Liefert Eigenschaftszeiger zurueck
    decltype(pData) GetDataPtr()
    {
        return pData;
    }
};

// Definition eines Any-Objekts
Any anyObj;

int main()
{
    // Eigenschaftszeiger initialisieren
    anyObj.Init();
    // und auslesen
    auto ptrData = anyObj.GetDataPtr();
    // Nun die Eigenschaft modifizieren und auslesen
    (anyObj.*ptrData) = 10;
    std::println("Datum: {}",(anyObj.*ptrData));
};

Datum: 10

Auch hierbei ist es möglich, auf die private-Eigenschaften zuzugreifen.