名前空間
変種
操作

非静的メンバ関数

提供: cppreference.com
< cpp‎ | language
 
 
C++言語
一般的なトピック
フロー制御
条件付き実行文
繰り返し文 (ループ)
ジャンプ文
関数
関数宣言
ラムダ関数宣言
inline 指定子
例外指定 (C++20未満)
noexcept 指定子 (C++11)
例外
名前空間
指定子
decltype (C++11)
auto (C++11)
alignas (C++11)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点
文字 - 文字列 - nullptr (C++11)
ユーザ定義 (C++11)
ユーティリティ
属性 (C++11)
typedef 宣言
型エイリアス宣言 (C++11)
キャスト
暗黙の変換 - 明示的な変換
static_cast - dynamic_cast
const_cast - reinterpret_cast
メモリ確保
クラス
クラス固有の関数特性
特別なメンバ関数
テンプレート
その他
 
 

非静的メンバ関数は、クラスのメンバ指定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 の非静的メンバ関数は、

1) X 型のオブジェクトに対してクラスメンバアクセス演算子を用いて呼べます。
2) X から派生したクラスのオブジェクトに対して呼べます。
3) X のメンバ関数の本体内から直接呼べます。
4) 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 です。
}

非静的メンバ関数は、参照修飾子なしで、左辺値参照修飾子 (引数リストの後の & トークン) 付きで、または右辺値参照修飾子 (引数リストの後の && トークン) 付きで、宣言できます。 オーバーロード解決の際、クラス 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 を用いて定義できる関数は、特別なメンバ関数および比較演算子 (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

[編集] 関連項目