非静态成员函数
非静态成员函数是在类的成员说明中声明的函数,没有 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
基类的非类型非静态成员的 标识符表达式 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
基类的静态成员、枚举器或嵌套类型的非限定标识符将被转换为相应的限定标识符
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 }
[edit] 带 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 起) |
[edit] 虚函数和纯虚函数
非静态成员函数可以声明为虚函数或纯虚函数。有关详细信息,请参见 虚函数 和 抽象类。
显式对象成员函数对于没有使用 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& } 这使得能够对常量和非常量成员函数进行重复数据消除,有关示例,请参见 数组下标运算符。 此外,显式对象参数推断为派生类型,这简化了 CRTP // a CRTP trait struct add_postfix_increment { template<typename Self> auto operator++(this Self&& self, int) { auto tmp = self; // Self deduces to "some_type" ++self; return tmp; } }; struct some_type : add_postfix_increment { some_type& operator++() { ... } }; 在显式对象成员函数体内部,不能使用 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 起) |
[edit] 特殊成员函数
一些成员函数是特殊的:在某些情况下,即使没有由用户定义,它们也会由编译器定义。他们是
(自 C++11 起) |
(自 C++11 起) |
特殊成员函数 以及 比较运算符(自 C++20 起) 是唯一可以默认的函数,即使用 = default 而不是函数体来定义(有关详细信息,请参阅其页面)。
[edit] 注释
功能测试宏 | 值 | Std | 功能 |
---|---|---|---|
__cpp_ref_qualifiers |
200710L | (C++11) | 引用限定符 |
__cpp_explicit_this_parameter |
202110L | (C++23) | 显式对象参数 (推断 this ) |
[edit] 示例
#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
[edit] 缺陷报告
以下行为改变缺陷报告已追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布的行为 | 正确行为 |
---|---|---|---|
CWG 194 | C++98 | 模棱两可的是,非静态成员函数 是否可以与封闭类名相同 |
添加了明确的命名限制 |