非静态成员函数
非静态成员函数是在类的成员说明中不带 static
或 friend
说明符声明的函数。(这些关键词的效果见静态成员函数与友元声明)
class S { int mf1(); // 非静态成员函数声明 void mf2() volatile, mf3() &&; // 可以有 cv 限定或引用限定 int mf4() const { return data; } // 可以内联定义 virtual void mf5() final; // 可以是虚函数,可以使用 final/override S() : data(12) {} // 构造函数也是成员函数 int data; }; int S::mf1() { return 7; } // 不内联定义就必须在命名空间定义
允许任何函数声明,外加非静态成员函数专用的语法元素:纯说明符,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
或 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 }
有 const 与 volatile 限定的成员函数
非静态成员函数可以带 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 成员函数 int operator[](int idx) const { // this 具有类型 const Array* return data[idx]; // 变换为 (*this).data[idx]; } // 非 const 成员函数 int& operator[](int idx) { // this 具有类型 Array* return data[idx]; // 变换为 (*this).data[idx] } }; int main() { Array a(10); a[1] = 1; // OK:a[1] 的类型是 int& const Array ca(10); ca[1] = 2; // 错误:ca[1] 的类型是 int }
有引用限定的成员函数非静态成员函数可以不带引用限定符,带有左值引用限定符(函数名后的
注意:与 cv 限定性不同,引用限定性不改变 |
(C++11 起) |
虚函数和纯虚函数
非静态成员函数可以声明为虚或纯虚函数。细节见虚函数与抽象类。
特殊成员函数
构造函数和析构函数是非静态成员函数,在其声明中使用特殊的语法(细节见其相应页面)。
一些成员函数是特殊的:在某些环境下,即使用户不定义编译器也会定义它们。它们是:
(C++11 起) |
(C++11 起) |
-
(C++20 前) (C++20 起) 特殊成员函数以及比较运算符 (C++20 起)是仅有的能被预置的函数,即使用 = default 替代函数体进行定义(细节见其相应页面)。
示例
运行此代码#include <iostream> #include <string> #include <utility> #include <exception> 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 << "调用构造函数1,data = " << data << '\n'; } // 此构造函数拥有 catch 子句 S::S(std::string str) try : data(std::stoi(str)) { std::cout << "调用构造函数2,data = " << data << '\n'; } catch(const std::exception&) { std::cout << "构造函数2失败,字符串'" << 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("不是数字"); } catch(const std::exception&) {} std::cout << s2.getData() << '\n'; D d2(3, 4); d2 = d1; // OK :赋值给左值 // D(5) = d1; // 错误:没有适合的 operator= 重载 }
输出:
调用构造函数1,data = 1 调用构造函数2,data = 2 构造函数2失败,字符串'不是数字' 2 调用构造函数1,data = 3
参阅