非静的メンバ関数
非静的メンバ関数は、クラスのメンバ指定で 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; } // インラインで定義されていない場合は、名前空間で定義する必要があります。
任意の関数宣言が使用できます。 また、非静的メンバ関数に対しては追加の構文要素 final および override 指定子、純粋指定子、 cv 修飾、参照修飾、およびメンバ初期化子リストが使用できます。
クラス 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 です。
}
|
非静的メンバ関数は、参照修飾子なしで、左辺値参照修飾子 (引数リストの後の
#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 ポインタの性質を変更しません。 右辺値参照修飾された関数内の |
(C++11以上) |
仮想関数および純粋仮想関数
非静的メンバ関数は仮想または純粋仮想として宣言できます。 詳細については仮想関数および抽象クラスを参照してください。
特別なメンバ関数
コンストラクタおよびデストラクタは宣言に特別な構文を使用する非静的メンバ関数です (詳細はそれぞれのページを参照してください)。
一部のメンバ関数は特別です。 特定の状況下において、それらはたとえユーザが定義しなくてもコンパイラによって定義されます。 特別なメンバ関数は以下の通りです。
| (C++11以上) |
| (C++11以上) |
デフォルト化できる関数、つまり関数の本体の代わりに = default を用いて定義できる関数は、特別なメンバ関数および比較演算子 (C++20以上)だけです (詳細はそれぞれのページを参照してください)。
例
#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