Nicht-statische Memberfunktionen
Eine nicht-statische Memberfunktion ist eine Funktion, die in einer Member-Spezifikation einer Klasse deklariert ist, ohne einen static oder friend-Spezifizierer (siehe statische Memberfunktionen und friend-Deklaration für die Auswirkung dieser Schlüsselwörter).
class S { int mf1(); // non-static member function declaration void mf2() volatile, mf3() &&; // can have cv-qualifiers and/or a reference-qualifier // the declaration above is equivalent to two separate declarations: // void mf2() volatile; // void mf3() &&; int mf4() const { return data; } // can be defined inline virtual void mf5() final; // can be virtual, can use final/override S() : data(12) {} // constructors are member functions too int data; }; int S::mf1() { return 7; } // if not defined inline, has to be defined at namespace
Konstruktoren, Destruktoren und Konvertierungsfunktionen verwenden spezielle Syntaxen für ihre Deklarationen. Die auf dieser Seite beschriebenen Regeln gelten möglicherweise nicht für diese Funktionen. Einzelheiten finden Sie auf den jeweiligen Seiten.
|
Eine explizite Objekt-Memberfunktion ist eine nicht-statische Memberfunktion mit einem expliziten Objektparameter. |
(seit C++23) |
Eine implizite Objekt-Memberfunktion ist eine nicht-statische Memberfunktion ohne expliziten Objektparameter (vor C++23 war dies die einzige Art von nicht-statischer Memberfunktion und wurde daher in der Literatur als "nicht-statische Memberfunktion" bezeichnet).
Inhalt |
[bearbeiten] Erläuterung
Alle Funktionsdeklarationen sind zulässig, mit zusätzlichen Syntaxelementen, die nur für nicht-statische Memberfunktionen verfügbar sind: reine Spezifizierer, cv-Qualifizierer, Ref-Qualifizierer, final und override Spezifizierer(seit C++11) und Member-Initialisierungslisten.
Eine nicht-statische Memberfunktion der Klasse X kann aufgerufen werden
X unter Verwendung des Klassen-Member-ZugriffsoperatorsXX abgeleiteten KlasseDer Aufruf einer nicht-statischen Memberfunktion der Klasse X für ein Objekt, das nicht vom Typ X oder einer von X abgeleiteten Klasse ist, führt zu undefiniertem Verhalten.
Innerhalb des Körpers einer nicht-statischen Memberfunktion von X wird jeder id-Ausdruck e (z. B. ein Bezeichner), der sich zu einem Nicht-Typ-Nicht-Statik-Member von X oder einer Basisklasse von X auflöst, in einen Member-Zugriffsausdruck (*this).e umgewandelt (es sei denn, er ist bereits Teil eines Member-Zugriffsausdrucks). Dies geschieht nicht im Kontext von Vorlagendefinitionen, daher muss ein Name möglicherweise explizit mit this-> präfixiert werden, um abhängig zu werden.
struct S { int n; void f(); }; void S::f() { n = 1; // transformed to (*this).n = 1; } int main() { S s1, s2; s1.f(); // changes s1.n }
Innerhalb des Körpers einer nicht-statischen Memberfunktion von X wird jeder nicht qualifizierte Bezeichner, der sich zu einem statischen Member, einem Enumerator oder einem verschachtelten Typ von X oder einer Basisklasse von X auflöst, in den entsprechenden qualifizierten Bezeichner umgewandelt.
struct S { static int n; void f(); }; void S::f() { n = 1; // transformed to S::n = 1; } int main() { S s1, s2; s1.f(); // changes S::n }
[bearbeiten] Memberfunktionen mit cv-Qualifizierern
Eine implizite Objekt-Memberfunktion kann mit einer cv-Qualifiziersequenz (const, volatile oder einer Kombination aus const und volatile) deklariert werden. Diese Sequenz erscheint nach der Parameterliste in der Funktionsdeklaration. Funktionen mit unterschiedlichen cv-Qualifiziersequenzen (oder keiner Sequenz) haben unterschiedliche Typen und können sich daher gegenseitig überladen.
Im Körper einer Funktion mit einer cv-Qualifiziersequenz ist *this cv-qualifiziert, z. B. in einer Memberfunktion mit const-Qualifizierer dürfen nur andere Memberfunktionen mit const-Qualifizierer normal aufgerufen werden. Eine Memberfunktion ohne const-Qualifizierer kann immer noch aufgerufen werden, wenn const_cast angewendet wird oder über einen Zugriffspfad, der this nicht einschließt.
#include <vector> struct Array { std::vector<int> data; Array(int sz) : data(sz) {} // const member function int operator[](int idx) const { // the this pointer has type const Array* return data[idx]; // transformed to (*this).data[idx]; } // non-const member function int& operator[](int idx) { // the this pointer has type Array* return data[idx]; // transformed to (*this).data[idx] } }; int main() { Array a(10); a[1] = 1; // OK: the type of a[1] is int& const Array ca(10); ca[1] = 2; // Error: the type of ca[1] is int }
Memberfunktionen mit Ref-QualifizierernEine implizite Objekt-Memberfunktion kann ohne Ref-Qualifizierer, mit einem Lvalue-Ref-Qualifizierer (das Token
Hinweis: Im Gegensatz zur cv-Qualifizierung ändert die Ref-Qualifizierung nicht die Eigenschaften des |
(seit C++11) |
[bearbeiten] Virtuelle und reine virtuelle Funktionen
Eine nicht-statische Memberfunktion kann als virtuell oder rein virtuell deklariert werden. Details finden Sie unter virtuelle Funktionen und abstrakte Klassen.
Explizite Objekt-MemberfunktionenFür eine nicht-statische, nicht-virtuelle Memberfunktion, die nicht mit cv-Qualifizierer oder Ref-Qualifizierer deklariert ist, kann ihr erster Parameter, falls es sich nicht um einen Funktionsparameterpaket handelt, ein expliziter Objektparameter sein (gekennzeichnet mit dem Präfix-Schlüsselwort this). struct X { void foo(this X const& self, int i); // same as void foo(int i) const &; // void foo(int i) const &; // Error: already declared void bar(this X self, int i); // pass object by value: makes a copy of “*this” }; Für Memberfunktionsvorlagen ermöglicht der explizite Objektparameter die Ableitung von Typ und Wertkategorie. Dieses Sprachmerkmal wird als "deducing this" bezeichnet. struct X { template<typename Self> void foo(this Self&&, int); }; struct D : X {}; void ex(X& x, D& d) { x.foo(1); // Self = X& move(x).foo(2); // Self = X d.foo(3); // Self = D& } Dies macht es möglich, konstante und nicht-konstante Memberfunktionen zu deduplizieren. Siehe Array-Subskriptionsoperator als Beispiel. Innerhalb des Körpers einer expliziten Objekt-Memberfunktion kann der this-Zeiger nicht verwendet werden: Alle Memberzugriffe müssen über den ersten Parameter erfolgen, wie bei statischen Memberfunktionen. struct C { void bar(); void foo(this C c) { auto x = this; // error: no this bar(); // error: no implicit this-> c.bar(); // ok } }; Ein Zeiger auf eine explizite Objekt-Memberfunktion ist ein gewöhnlicher Funktionszeiger, kein Memberzeiger. struct Y { int f(int, int) const&; int g(this Y const&, int, int); }; auto pf = &Y::f; pf(y, 1, 2); // error: pointers to member functions are not callable (y.*pf)(1, 2); // ok std::invoke(pf, y, 1, 2); // ok auto pg = &Y::g; pg(y, 3, 4); // ok (y.*pg)(3, 4); // error: “pg” is not a pointer to member function std::invoke(pg, y, 3, 4); // ok |
(seit C++23) |
[bearbeiten] Spezielle Memberfunktionen
Einige Memberfunktionen sind speziell: Unter bestimmten Umständen werden sie vom Compiler definiert, auch wenn sie nicht vom Benutzer definiert wurden. Dies sind:
| (seit C++11) |
| (seit C++11) |
- Destruktor(bis C++20)Prospektiver Destruktor(seit C++20)
Spezielle Memberfunktionen sowie die Vergleichsoperatoren(seit C++20) sind die einzigen Funktionen, die standardmäßig definiert werden können, d. h. mit = default anstelle des Funktionskörpers (Einzelheiten finden Sie auf ihren Seiten).
[bearbeiten] Anmerkungen
| Feature-Testmakro | Wert | Std | Feature |
|---|---|---|---|
__cpp_ref_qualifiers |
200710L |
(C++11) | Referenz-Qualifizierer |
__cpp_explicit_this_parameter |
202110L |
(C++23) | expliziter Objektparameter (deducing this) |
[bearbeiten] Beispiel
#include <exception> #include <iostream> #include <string> #include <utility> struct S { int data; // simple converting constructor (declaration) S(int val); // simple explicit constructor (declaration) explicit S(std::string str); // const member function (definition) virtual int getData() const { return data; } }; // definition of the constructor S::S(int val) : data(val) { std::cout << "ctor1 called, data = " << data << '\n'; } // this constructor has a catch clause S::S(std::string str) try : data(std::stoi(str)) { std::cout << "ctor2 called, data = " << data << '\n'; } catch(const std::exception&) { std::cout << "ctor2 failed, string was '" << str << "'\n"; throw; // ctor's catch clause should always rethrow } struct D : S { int data2; // constructor with a default argument D(int v1, int v2 = 11) : S(v1), data2(v2) {} // virtual member function int getData() const override { return data * data2; } // lvalue-only assignment operator D& operator=(D other) & { std::swap(other.data, data); std::swap(other.data2, data2); return *this; } }; int main() { D d1 = 1; S s2("2"); try { S s3("not a number"); } catch(const std::exception&) {} std::cout << s2.getData() << '\n'; D d2(3, 4); d2 = d1; // OK: assignment to lvalue // D(5) = d1; // ERROR: no suitable overload of operator= }
Ausgabe
ctor1 called, data = 1 ctor2 called, data = 2 ctor2 failed, string was 'not a number' 2 ctor1 called, data = 3
[bearbeiten] Defect Reports
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 194 | C++98 | unklar, ob eine nicht-statische Memberfunktion denselben Namen wie der umschließende Klassenname haben könnte |
Einschränkung der expliziten Benennung hinzugefügt |