Non-static member functions
Eine nicht-statische Memberfunktion ist eine Funktion, die in einer
Memberspezifikation
einer Klasse ohne einen
static
- oder
friend
-Spezifizierer deklariert wird
(siehe
statische Memberfunktionen
und
Friend-Deklaration
für die Auswirkungen dieser Schlüsselwörter).
class S { int mf1(); // Deklaration einer nicht-statischen Member-Funktion void mf2() volatile, mf3() &&; // kann CV-Qualifizierer und/oder Referenzqualifizierer haben // die obige Deklaration entspricht zwei separaten Deklarationen: // void mf2() volatile; // void mf3() &&; int mf4() const { return data; } // kann inline definiert werden virtual void mf5() final; // kann virtuell sein, kann final/override verwenden S() : data(12) {} // Konstruktoren sind ebenfalls Member-Funktionen int data; }; int S::mf1() { return 7; } // wenn nicht inline definiert, muss im Namespace definiert werden
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. Weitere Einzelheiten finden Sie auf den jeweiligen Seiten.
|
Eine explizite Objekt-Memberfunktion ist eine nicht-statische Memberfunktion mit einem expliziten Objektparameter . |
(since C++23) |
Eine implizite Objekt-Memberfunktion ist eine nicht-statische Memberfunktion ohne expliziten Objektparameter (vor C++23 war dies die einzige Art von nicht-statischen Memberfunktionen und wurde daher in der Literatur als "nicht-statische Memberfunktion" bezeichnet).
Inhaltsverzeichnis |
Erklärung
Jede
Funktionsdeklaration
ist erlaubt, mit zusätzlichen Syntaxelementen, die nur für nicht-statische Memberfunktionen verfügbar sind:
Pure-Specifiers
, CV-Qualifiers
, Ref-Qualifiers,
final
und
override
Specifiers
(seit C++11)
, und
Member-Initialisierungslisten
.
Eine nicht-statische Memberfunktion der Klasse
X
kann aufgerufen werden
X
unter Verwendung des Klassen-Member-Zugriffsoperators
X
X
abgeleiteten Klasse
Das Aufrufen einer nicht-statischen Memberfunktion der Klasse
X
für ein Objekt, das nicht vom Typ
X
oder eines von
X
abgeleiteten Typs ist, führt zu undefiniertem Verhalten.
Im Rumpf einer nicht-statischen Memberfunktion von
X
wird jeder
Id-Ausdruck
e
(z.B. ein Bezeichner), der zu einem Nicht-Typ-Nicht-Statischen-Member von
X
oder einer Basisklasse von
X
aufgelöst wird, in einen Member-Zugriffsausdruck
(
*
this
)
.
e
transformiert (sofern er nicht bereits Teil eines Member-Zugriffsausdrucks ist). Dies tritt nicht im Template-Definitionskontext auf, sodass ein Name explizit mit
this
-
>
präfixiert werden muss, um
abhängig
zu werden.
struct S { int n; void f(); }; void S::f() { n = 1; // umgewandelt zu (*this).n = 1; } int main() { S s1, s2; s1.f(); // ändert s1.n }
Im Rumpf einer nicht-statischen Memberfunktion von
X
wird jeder unqualifizierte Bezeichner, der zu einem statischen Member, einem Enumerator oder einem geschachtelten Typ von
X
oder einer Basisklasse von
X
aufgelöst wird, in den entsprechenden qualifizierten Bezeichner transformiert:
struct S { static int n; void f(); }; void S::f() { n = 1; // umgewandelt zu S::n = 1; } int main() { S s1, s2; s1.f(); // ändert S::n }
Memberfunktionen mit cv-Qualifizierern
Eine implizite Objekt-Memberfunktion kann mit einer CV-Qualifizierer -Sequenz ( const , volatile , oder einer Kombination aus const und volatile ) deklariert werden. Diese Sequenz erscheint nach der Parameterliste in der Funktionsdeklaration . Funktionen mit unterschiedlichen CV-Qualifizierer-Sequenzen (oder ohne Sequenz) haben unterschiedliche Typen und können sich daher gegenseitig überladen.
Im Rumpf einer Funktion mit einer CV-Qualifizierer-Sequenz ist
*
this
CV-qualifiziert, z.B. in einer Elementfunktion mit
const
-Qualifizierer können nur andere Elementfunktionen mit
const
-Qualifizierer normal aufgerufen werden. Eine Elementfunktion ohne
const
-Qualifizierer kann dennoch aufgerufen werden, wenn
const_cast
angewendet wird oder über einen Zugriffspfad, der nicht
this
beinhaltet.
#include <vector> struct Array { std::vector<int> data; Array(int sz) : data(sz) {} // konstante Member-Funktion int operator[](int idx) const { // der this-Zeiger hat den Typ const Array* return data[idx]; // transformiert zu (*this).data[idx]; } // nicht-konstante Member-Funktion int& operator[](int idx) { // der this-Zeiger hat den Typ Array* return data[idx]; // transformiert zu (*this).data[idx] } }; int main() { Array a(10); a[1] = 1; // OK: der Typ von a[1] ist int& const Array ca(10); ca[1] = 2; // Fehler: der Typ von ca[1] ist int }
Memberfunktionen mit Ref-Qualifier
Eine implizite Objekt-Memberfunktion kann ohne Ref-Qualifier, mit einem Lvalue-Ref-Qualifier (das Token
Hinweis: Im Gegensatz zur CV-Qualifizierung ändert die Ref-Qualifizierung nicht die Eigenschaften des
|
(seit C++11) |
Virtuelle und rein virtuelle Funktionen
Eine nicht-statische Memberfunktion kann als virtual oder pure virtual deklariert werden. Weitere Details finden Sie unter virtual functions und abstract classes .
Explizite Objekt-MemberfunktionenFür eine nicht-statische, nicht-virtuelle Memberfunktion, die nicht mit CV-Qualifizierer oder Ref-Qualifizierer deklariert wurde, kann ihr erster Parameter, sofern es sich nicht um ein Funktionsparameter-Paket handelt, ein expliziter Objektparameter sein (gekennzeichnet durch das vorangestellte 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 Memberfunktions-Templates ermöglicht der explizite Objektparameter die Deduktion von Typ und Wertkategorie. Diese Sprachfunktion wird „Deduzieren von this “ genannt: 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 ermöglicht die Deduplizierung von const- und nicht-const-Memberfunktionen. Siehe Array-Subscript-Operator für ein Beispiel. Innerhalb des Rumpfes 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 Zeiger auf Member: 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) |
Spezielle Elementfunktionen
Einige Memberfunktionen sind speziell : unter bestimmten Umständen werden sie vom Compiler definiert, selbst wenn sie nicht vom Benutzer definiert wurden. Sie sind:
| (seit C++11) |
| (seit C++11) |
- Destruktor (bis C++20) Prospektiver Destruktor (seit C++20)
Spezielle Elementfunktionen zusammen mit den Vergleichsoperatoren (seit C++20) sind die einzigen Funktionen, die standardmäßig definiert werden können, das heißt, definiert mit = default anstelle des Funktionsrumpfs (siehe deren Seiten für Details).
Hinweise
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_ref_qualifiers
|
200710L
|
(C++11) | Ref-Qualifier |
__cpp_explicit_this_parameter
|
202110L
|
(C++23) |
Expliziter Objektparameter
(
Ableitung von
this
)
|
Beispiel
#include <exception> #include <iostream> #include <string> #include <utility> struct S { int data; // einfacher konvertierender Konstruktor (Deklaration) S(int val); // einfacher expliziter Konstruktor (Deklaration) explicit S(std::string str); // konstante Memberfunktion (Definition) virtual int getData() const { return data; } }; // Definition des Konstruktors S::S(int val) : data(val) { std::cout << "ctor1 called, data = " << data << '\n'; } // dieser Konstruktor hat eine Catch-Klausel 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; // Catch-Klausel des Konstruktors sollte immer erneut werfen } struct D : S { int data2; // Konstruktor mit einem Standardargument D(int v1, int v2 = 11) : S(v1), data2(v2) {} // virtuelle Memberfunktion int getData() const override { return data * data2; } // Nur-Lvalue-Zuweisungsoperator 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: Zuweisung an Lvalue // D(5) = d1; // FEHLER: keine passende Überladung von operator= }
Ausgabe:
ctor1 called, data = 1 ctor2 called, data = 2 ctor2 failed, string was 'not a number' 2 ctor1 called, data = 3
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| CWG 194 | C++98 |
unklar, ob eine nicht-statische Memberfunktion
denselben Namen wie der umschließende Klassenname haben konnte |
explizite Namenseinschränkung hinzugefügt |