非静态成员函数

来自cppreference.com
< cpp‎ | language

非静态成员函数是声明于类的成员指定中,不带 staticfriend 指定符的函数。

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; } // 若不内联定义,则必须定义于命名空间

允许任何函数声明,而且附加的语法元素仅可用于非静态成员函数: finaloverride 指定符、纯指定符、 cv 限定符、引用限定符,及成员初始化列表

类 X 的非静态成员函数可以

1) 对 X 类型的对象使用类成员访问运算符调用
2)导出自 X 的类的对象调用
3) 直接从 X 的成员函数体内调用
4) 直接从导出自 X 的类的成员函数体内调用

在任何其他类型的对象上调用类 X 的成员函数导致未定义行为。

在 X 的非静态成员函数的体内,任何解析为 X 或 X 的基类的非类型非静态成员的 id 表达式 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 基类的嵌套类型的非限定 id ,均被变为对应的限定 id 。

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 成员函数仍可调用,若应用 const_cast 或通过不涉及 this 的访问路径。)

#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];
    }
    // non-const member function
    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
}

非静态成员函数可声明无引用限定符、有左值引用限定符(函数名后的 & 记号)或右值引用限定符(函数名后的 && 记号)。在重载解析中,按下列方式对待类 X 的非静态 cv 限定成员:

  • 无引用限定符:隐式对象参数拥有 cv 限定的 X 的左值引用类型,并额外允许绑定右值隐含对象参数
  • 左值引用限定符:隐式对象参数拥有到 cv 限定的 X 的左值引用类型
  • 右值引用限定符:隐式对象参数拥有到 cv 限定的 X 的右值引用类型
#include <iostream>
struct S {
    void f() & { std::cout << "lvalue\n"; }
    void f() &&{ std::cout << "rvalue\n"; }
};
 
int main(){
    S s;
    s.f();            // 打印“ lvalue ”
    std::move(s).f(); // 打印“ rvalue ”
    S().f();          // 打印“ rvalue ”
}

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

(C++11 起)

[编辑] 虚及纯虚成员函数

非静态成员函数可声明为纯虚。细节见虚函数抽象类

[编辑] 特殊成员函数

构造函数析构函数,是为其声明使用特殊语法的非公开成员函数(细节见其页面)。

一些成员函数是特殊的:在某些环境下,它们为编译器所定义,即使用户不定义。它们是:

特殊成员函数是仅有能被默认化的函数,即使用 = default 替代函数体定义(细节见其页面)

[编辑] 示例

#include <iostream>
#include <string>
#include <utility>
#include <exception>
 
struct S {
    int data;
 
    // 简单转换构造函数(声明)
    S(int val);
 
    // 简单 explicit 构造函数(声明)
    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; // OK :赋值给左值
//   D(5) = d1; // 错误:无适合的 operator= 重载
}

输出:

ctor1 called, data = 1
ctor2 called, data = 2
ctor2 failed, string was 'not a number'
2
ctor1 called, data = 3

[编辑] 参阅