Non-static member functions
非静态成员函数是在类的
成员声明
中未使用
static
或
friend
说明符声明的函数
(关于这些关键字的作用,请参阅
静态成员函数
和
友元声明
)。
class S { int mf1(); // 非静态成员函数声明 void mf2() volatile, mf3() &&; // 可包含cv限定符和/或引用限定符 // 上述声明等价于两个独立声明: // void mf2() volatile; // void mf3() &&; int mf4() const { return data; } // 可内联定义 virtual void mf5() final; // 可为虚函数,可使用final/override S() : data(12) {} // 构造函数同样属于成员函数 int data; }; int S::mf1() { return 7; } // 若非内联定义,需在命名空间作用域定义
构造函数 、 析构函数 和 转换函数 使用特殊的语法进行声明。本页描述的规则可能不适用于这些函数。详情请参阅各自的页面。
|
显式对象成员函数 是带有 显式对象形参 的非静态成员函数。 |
(since 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; // 转换为 (*this).n = 1; } int main() { S s1, s2; s1.f(); // 修改 s1.n }
在
X
的非静态成员函数体内,任何解析为
X
或其基类的静态成员、枚举项或嵌套类型的非限定标识符,都会被转换为对应的限定标识符:
struct S { static int n; void f(); }; void S::f() { n = 1; // 转换为 S::n = 1; } int main() { S s1, s2; s1.f(); // 修改 S::n }
带 cv 限定符的成员函数
隐式对象成员函数可以通过 cv限定符 序列( 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) {} // 常量成员函数 int operator[](int idx) const { // this 指针的类型为 const Array* return data[idx]; // 转换为 (*this).data[idx]; } // 非常量成员函数 int& operator[](int idx) { // this 指针的类型为 Array* return data[idx]; // 转换为 (*this).data[idx] } }; int main() { Array a(10); a[1] = 1; // 正确:a[1] 的类型是 int& const Array ca(10); ca[1] = 2; // 错误:ca[1] 的类型是 int }
带引用限定符的成员函数
隐式对象成员函数可以声明为无引用限定符、带左值引用限定符(参数列表后的
注意:与 cv 限定不同,引用限定不会改变
|
(C++11 起) |
虚函数与纯虚函数
非静态成员函数可被声明为 virtual 或 pure virtual 。详见 虚函数 与 抽象类 说明。
显式对象成员函数对于未用 cv 限定符或引用限定符声明的非静态非虚成员函数,其第一个参数(若非 函数形参包 )可以是 显式对象形参 (以前缀关键字 this 表示): struct X { void foo(this X const& self, int i); // 等同于 void foo(int i) const &; // void foo(int i) const &; // 错误:已声明 void bar(this X self, int i); // 按值传递对象:生成“*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; // 错误:不存在 this bar(); // 错误:无隐式 this-> c.bar(); // 正确 } }; 指向显式对象成员函数的指针是普通函数指针,而非成员指针: struct Y { int f(int, int) const&; int g(this Y const&, int, int); }; auto pf = &Y::f; pf(y, 1, 2); // 错误:指向成员函数的指针不可调用 (y.*pf)(1, 2); // 正确 std::invoke(pf, y, 1, 2); // 正确 auto pg = &Y::g; pg(y, 3, 4); // 正确 (y.*pg)(3, 4); // 错误:“pg”不是指向成员函数的指针 std::invoke(pg, y, 3, 4); // 正确 |
(C++23 起) |
特殊成员函数
某些成员函数是 特殊 的:在特定情况下,即使使用者未定义,它们也会由编译器自动生成。这些函数包括:
| (自 C++11 起) |
| (自 C++11 起) |
特殊成员函数 以及 比较运算符 (自C++20起) 是唯一可被 缺省定义 的函数,即使用 = default 代替函数体进行定义(详见各函数说明页)。
注释
| 功能测试宏 | 值 | 标准 | 功能特性 |
|---|---|---|---|
__cpp_ref_qualifiers
|
200710L
|
(C++11) | 引用限定符 |
__cpp_explicit_this_parameter
|
202110L
|
(C++23) |
显式对象参数
(
推导
this
)
|
示例
#include <exception> #include <iostream> #include <string> #include <utility> struct S { int data; // 简单转换构造函数(声明) S(int val); // 简单显式构造函数(声明) explicit S(std::string str); // const 成员函数(定义) virtual int getData() const { return data; } }; // 构造函数的定义 S::S(int val) : data(val) { std::cout << "ctor1 called, data = " << data << '\n'; } // 此构造函数包含 catch 子句 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 子句应始终重新抛出 } struct D : S { int data2; // 带默认参数的构造函数 D(int v1, int v2 = 11) : S(v1), data2(v2) {} // 虚成员函数 int getData() const override { return data * data2; } // 仅限左值的赋值运算符 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; // 正确:对左值进行赋值 // D(5) = d1; // 错误:没有合适的 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 |
未明确非静态成员函数
是否可与所在类同名 |
已添加显式命名限制 |