非静态成员函数

C++

编译器支持

自由(freestanding)与宿主(hosted)

语言

标准库

标准库头文件

具名要求

特性测试宏 (C++20)

语言支持库

概念库 (C++20)

诊断库

内存管理库

元编程库 (C++11)

通用工具库

容器库

迭代器库

范围库 (C++20)

算法库

字符串库

文本处理库

数值库

日期和时间库

输入/输出库

文件系统库 (C++17)

并发支持库 (C++11)

执行控制库 (C++26)

技术规范

符号索引

外部库

[编辑] C++ 语言

通用主题

预处理器

注释

关键词

转义序列

流程控制

条件执行语句

if

switch

迭代语句(循环)

for

range-for (C++11)

while

do-while

跳转语句

continue - break

goto - return

函数

函数声明

Lambda 函数表达式

inline 说明符

动态异常规范 (直到 C++17*)

noexcept 说明符 (C++11)

异常

throw 表达式

try 块

catch 处理程序

命名空间

命名空间声明

命名空间别名

类型

基本类型

枚举类型

函数类型

类/结构体类型

联合类型

说明符

const/volatile

decltype (C++11)

auto (C++11)

constexpr (C++11)

consteval (C++20)

constinit (C++20)

存储期说明符

初始化

默认初始化

值初始化

零初始化

复制初始化

直接初始化

聚合初始化

列表初始化 (C++11)

常量初始化

引用初始化

表达式

值类别

求值顺序

运算符

运算符优先级

替代表示

字面量

布尔 - 整型 - 浮点型

字符 - 字符串 - nullptr (C++11)

用户定义 (C++11)

工具

属性 (C++11)

类型

typedef 声明

类型别名声明 (C++11)

类型转换

隐式转换

static_cast

const_cast

显式转换

dynamic_cast

reinterpret_cast

内存分配

new 表达式

delete 表达式

类声明

构造函数

this 指针

访问说明符

friend 说明符

类特有的函数属性

虚函数

override 说明符 (C++11)

final 说明符 (C++11)

explicit (C++11)

static

特殊成员函数

默认构造函数

复制构造函数

移动构造函数 (C++11)

复制赋值

移动赋值 (C++11)

析构函数

模板

类模板

函数模板

模板特化

参数包 (C++11)

杂项

内联汇编

C++ 历史

[编辑] 类

通用

概览

class/struct 类型

union 类型

注入类名

类属性说明符 (C++26)

成员

数据成员

静态成员

this 指针

嵌套类

成员模板

位域

using-声明

成员函数

成员访问说明符

构造函数和成员初始化列表

默认成员初始化器 (C++11)

friend 说明符

explicit 说明符

转换构造函数

特殊成员函数

默认构造函数

复制构造函数

移动构造函数 (C++11)

复制赋值运算符

移动赋值运算符 (C++11)

析构函数

继承

基类和派生类

空基类优化 (EBO)

虚成员函数

纯虚函数和抽象类

override 说明符 (C++11)

final 说明符 (C++11)

[编辑]

非静态成员函数是在类的成员声明中声明的函数,没有 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 之前,这是唯一一种非静态成员函数,因此在文献中被称为“非静态成员函数”)。

目录

1 解释

1.1 带 cv 限定符的成员函数

1.2 带引用限定符的成员函数

1.3 虚函数和纯虚函数

1.4 显式对象成员函数

1.5 特殊成员函数

2 注意

3 示例

4 缺陷报告

5 另请参阅

[编辑] 说明

允许任何函数声明,并附加仅适用于非静态成员函数的语法元素:纯说明符、cv 限定符、引用限定符、final 和 override 说明符(C++11 起)以及成员初始化列表。

类 X 的非静态成员函数可以按以下方式调用:

1) 对于类型为 X 的对象,使用类成员访问运算符

2) 对于从 X 派生的类的对象

3) 直接从 X 的成员函数体内

4) 直接从从 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

struct Array

{

std::vector 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

}

带引用限定符的成员函数

隐式对象成员函数可以声明为不带引用限定符,带左值引用限定符(参数列表后的标记 &)或右值引用限定符(参数列表后的标记 &&)。在重载决议期间,带类 X 的 cv 限定符序列的隐式对象成员函数按以下方式处理:

不带引用限定符:隐式对象参数的类型是 cv 限定 X 的左值引用,并且还允许绑定右值隐含对象参数左值引用限定符:隐式对象参数的类型是 cv 限定 X 的左值引用右值引用限定符:隐式对象参数的类型是 cv 限定 X 的右值引用

#include

struct S

{

void f() & { std::cout << "lvalue\n"; }

void f() && { std::cout << "rvalue\n"; }

};

int main()

{

S s;

s.f(); // prints "lvalue"

std::move(s).f(); // prints "rvalue"

S().f(); // prints "rvalue"

}

注意:与 cv 限定不同,引用限定不会改变this指针的属性:在右值引用限定函数中,*this 仍然是一个左值表达式。

(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

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 前)预期析构函数(C++20 起)

特殊成员函数以及比较运算符(C++20 起)是唯一可以被默认的函数,即使用 = default 而不是函数体来定义(详见其页面)。

[编辑] 注意

功能测试宏

标准

特性

__cpp_ref_qualifiers

200710L

(C++11)

ref-限定符

__cpp_explicit_this_parameter

202110L

(C++23)

显式对象参数(推导 this)

[编辑] 示例

运行此代码

#include

#include

#include

#include

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

非静态成员函数是否可以与外围类同名

添加了显式命名限制

[编辑] 另请参见

非静态数据成员

静态数据成员