非靜態成員函式
非靜態成員函式是在類的成員宣告中宣告的函式,沒有 static
或 friend
說明符(有關這些關鍵字的作用,請參見靜態成員函式和友元宣告)。
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
建構函式、解構函式和轉換函式使用特殊的語法進行宣告。本頁描述的規則可能不適用於這些函式。詳情請參見各自的頁面。
顯式物件成員函式是沒有顯式物件引數的非靜態成員函式。 |
(C++23 起) |
隱式物件成員函式是沒有顯式物件引數的非靜態成員函式(在 C++23 之前,這是唯一一種非靜態成員函式,因此在文獻中被稱為“非靜態成員函式”)。
目錄 |
[編輯] 說明
允許任何函式宣告,並附加僅適用於非靜態成員函式的語法元素:純說明符、cv 限定符、引用限定符、final
和 override
說明符(C++11 起)以及成員初始化列表。
類 X
的非靜態成員函式可以按以下方式呼叫:
X
的物件,使用類成員訪問運算子X
的成員函式體內X
派生的類的成員函式體內在不是型別 X
或從 X
派生的型別的物件上呼叫類 X
的非靜態成員函式會觸發未定義行為。
在 X
的非靜態成員函式體內,任何解析為 X
或 X
的基類的非型別非靜態成員的id-expression e(例如識別符號)都會轉換為成員訪問表示式 (*this).e(除非它已經是成員訪問表示式的一部分)。這不會發生在模板定義上下文中,因此名稱可能需要顯式地加上 this-> 字首才能成為依賴名稱。
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 }
在 X
的非靜態成員函式體內,任何解析為 X
或 X
的基類的靜態成員、列舉器或巢狀型別的 unqualified-id 都會轉換為相應的 qualified-id。
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 }
[編輯] 帶 cv 限定符的成員函式
隱式物件成員函式可以宣告為帶cv 限定符序列(const、volatile,或 const 和 volatile 的組合),該序列出現在函式宣告的引數列表之後。具有不同 cv 限定符序列(或沒有序列)的函式具有不同的型別,因此可以相互過載。
在帶 cv 限定符序列的函式體內,*this 是 cv 限定的,例如,在帶 const 限定符的成員函式中,通常只能呼叫其他帶 const 限定符的成員函式。如果應用了const_cast
或透過不涉及this
的訪問路徑,仍然可以呼叫不帶 const 限定符的成員函式。
#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 }
帶引用限定符的成員函式隱式物件成員函式可以宣告為不帶引用限定符,帶左值引用限定符(引數列表後的標記
注意:與 cv 限定不同,引用限定不會改變 |
(C++11 起) |
[編輯] 虛擬函式和純虛擬函式
非靜態成員函式可以宣告為虛擬函式或純虛擬函式。詳情請參見虛擬函式和抽象類。
顯式物件成員函式對於未宣告 cv 限定符或引用限定符的非靜態非虛成員函式,其第一個引數(如果不是函式引數包)可以是顯式物件引數(用字首關鍵字 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” }; 對於成員函式模板,顯式物件引數允許推導型別和值類別,此語言特性稱為“推導 this” 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& } 這使得可以消除 const 和非 const 成員函式的重複,請參閱陣列下標運算子以獲取示例。 在顯式物件成員函式的函式體內,不能使用 this 指標:所有成員訪問都必須透過第一個引數完成,就像在靜態成員函式中一樣 struct C { void bar(); void foo(this C c) { auto x = this; // error: no this bar(); // error: no implicit this-> c.bar(); // ok } }; 指向顯式物件成員函式的指標是普通的函式指標,而不是成員指標 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 |
(C++23 起) |
[編輯] 特殊成員函式
有些成員函式是特殊的:在某些情況下,即使使用者沒有定義它們,編譯器也會定義它們。它們是:
(C++11 起) |
(C++11 起) |
特殊成員函式以及比較運算子(C++20 起)是唯一可以被預設的函式,即使用 = default 而不是函式體來定義(詳見其頁面)。
[編輯] 注意
功能測試宏 | 值 | 標準 | 特性 |
---|---|---|---|
__cpp_ref_qualifiers |
200710L |
(C++11) | ref-限定符 |
__cpp_explicit_this_parameter |
202110L |
(C++23) | 顯式物件引數(推導 this ) |
[編輯] 示例
#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= }
輸出
ctor1 called, data = 1 ctor2 called, data = 2 ctor2 failed, string was 'not a number' 2 ctor1 called, data = 3
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 194 | C++98 | 非靜態成員函式是否 可以與外圍類同名 |
添加了顯式命名限制 |