サーチ…


構文

  • //呼び出し中:

    • variable.member_function();
    • variable_pointer-> member_function();
  • //定義:

    • ret_type class_name :: member_function()cv-qualifiers {
      • 体;
    • }
  • //プロトタイプ:

    • class class_name {
      • virt-specifier ret_typeメンバー関数()cv-qualifiers virt-specifier-seq;
      • // virt-specifier:該当する場合、「仮想」。
      • // cv-qualifiers: "const"および/または "volatile"(該当する場合)
      • // virt-specifier-seq:該当する場合、「上書き」および/または「最終」。
    • }

備考

staticメンバ関数は、 class / struct / union体メンバ関数であり、特定のインスタンスに対して呼び出され、前記インスタンスに対して動作する。 staticメンバー関数とは異なり、インスタンスを指定しなければ呼び出すことはできません。

クラス、構造体、および共用体については、親トピックを参照しください。

非静的メンバー関数

classまたはstructは、メンバ関数とメンバ変数を持つことができます。これらの関数は、スタンドアロン関数とほとんど同じ構文を持ち、クラス定義の内側または外側で定義できます。クラス定義の外で定義されている場合は、関数の名前の先頭にクラスの名前とスコープ( ::演算子が付きます。

class CL {
  public:
    void  definedInside() {}
    void definedOutside();
};
void CL::definedOutside() {}

これらの関数は、ドット( . )演算子を持つクラスのインスタンス(またはインスタンスへの参照)、または矢印( -> )演算子を持つインスタンスへのポインタで呼び出され、各呼び出しは関数のインスタンスに結び付けられます呼び出されました。メンバ関数がインスタンス上で呼び出されると、( thisポインタを介して)そのインスタンスのすべてのフィールドにアクセスできますが、それらのインスタンスがパラメータとして提供されている場合にのみ他のインスタンスのフィールドにアクセスできます。

struct ST {
    ST(const std::string& ss = "Wolf", int ii = 359) : s(ss), i(ii) { }

    int get_i() const { return i; }
    bool compare_i(const ST& other) const { return (i == other.i); }

  private:
    std::string s;
    int i;
};
ST st1;
ST st2("Species", 8472);

int  i = st1.get_i(); // Can access st1.i, but not st2.i.
bool b = st1.compare_i(st2); // Can access st1 & st2.

これらの関数は、変数や関数のアクセス修飾子に関係なく、メンバ変数やその他のメンバ関数にアクセスすることができます。コンパイラがクラスのコンパイルを開始する前に、クラス定義全体を解析する必要があるため、メンバー変数やその前に宣言されたメンバー関数にアクセスすることで、順不同で記述することもできます。

class Access {
  public:
    Access(int i_ = 8088, int j_ = 8086, int k_ = 6502) : i(i_), j(j_), k(k_) {}

    int i;
    int get_k() const { return k; }
    bool private_no_more() const { return i_be_private(); }
  protected:
    int j;
    int get_i() const { return i; }
  private:
    int k;
    int get_j() const { return j; }
    bool i_be_private() const { return ((i > j) && (k < j)); }
};

カプセル化

メンバ関数の一般的な使用方法は、フィールドに直接アクセスするのではなく、 アクセッサ (一般にゲッタと呼ばれる)とミューテータ (一般にセッタと呼ばれる)を使用してカプセル化することです。

class Encapsulator {
    int encapsulated;

  public:
    int  get_encapsulated() const { return encapsulated; }
    void set_encapsulated(int e)  { encapsulated = e; }

    void some_func() {
        do_something_with(encapsulated);
    }
};

クラス内では、非静的メンバー関数によってencapsulatedされたものに自由にアクセスできます。クラス外で、それへのアクセスを使用して、メンバ関数によって調節されるget_encapsulated()それを読むし、 set_encapsulated()それを修正します。これにより、変数の意図しない変更が防止されます。これは、独立した関数が読み書きに使用されるためです。 [ゲッターとセッターがカプセル化を提供したり断ち切っているかどうかについて多くの議論があり、どちらの主張にも良い議論がある。このような熱い議論はこの例の範囲外です。]

名前の非表示とインポート

基本クラスがオーバーロードされた関数のセットを提供し、派生クラスがそのセットに別のオーバーロードを追加すると、基本クラスによって提供されるすべてのオーバーロードが非表示になります。

struct HiddenBase {
    void f(int) { std::cout << "int" << std::endl; }
    void f(bool) { std::cout << "bool" << std::endl; }
    void f(std::string) { std::cout << "std::string" << std::endl; }
};

struct HidingDerived : HiddenBase {
    void f(float) { std::cout << "float" << std::endl; }
};

// ...

HiddenBase hb;
HidingDerived hd;
std::string s;

hb.f(1);    // Output:  int
hb.f(true); // Output:  bool
hb.f(s);    // Output:  std::string;

hd.f(1.f);  // Output:  float
hd.f(3);    // Output:  float
hd.f(true); // Output:  float
hd.f(s);    // Error: Can't convert from std::string to float.

これは名前解決のルールによるものです:名前の検索中に正しい名前が見つかると、その名前のエンティティの正しいバージョンが見つからない場合でも( hd.f(s) );このため、派生クラスの関数をオーバーロードすると、名前参照によって基本クラスのオーバーロードが検出されなくなります。これを避けるために、using宣言を使用して、基本クラスから派生クラスに名前を「インポート」することができます。これにより、名前の参照時に使用できるようになります。

struct HidingDerived : HiddenBase {
     // All members named HiddenBase::f shall be considered members of HidingDerived for lookup.
    using HiddenBase::f;

    void f(float) { std::cout << "float" << std::endl; }
};

// ...

HidingDerived hd;

hd.f(1.f);  // Output:  float
hd.f(3);    // Output:  int
hd.f(true); // Output:  bool
hd.f(s);    // Output:  std::string

派生クラスが、using宣言で名前をインポートするだけでなく、基本クラスの関数と同じシグネチャで関数を宣言すると、基本クラス関数は自動的にオーバーライドまたは非表示になります。

struct NamesHidden {
    virtual void hide_me()      {}
    virtual void hide_me(float) {}
    void hide_me(int)           {}
    void hide_me(bool)          {}
};

struct NameHider : NamesHidden {
    using NamesHidden::hide_me;

    void hide_me()    {} // Overrides NamesHidden::hide_me().
    void hide_me(int) {} // Hides NamesHidden::hide_me(int).
};

インポートされたエンティティが基本クラスでpublicまたはprotectedれていれば、using宣言を使用してアクセス修飾子を変更することもできます。

struct ProMem {
  protected:
    void func() {}
};

struct BecomesPub : ProMem {
    using ProMem::func;
};

// ...

ProMem pm;
BecomesPub bp;

pm.func(); // Error: protected.
bp.func(); // Good.

同様に、継承階層の特定のクラスからメンバー関数を明示的に呼び出したい場合、関数を呼び出すときに関数名を修飾し、そのクラスを名前で指定することができます。

struct One {
    virtual void f() { std::cout << "One." << std::endl; }
};

struct Two : One {
    void f() override {
        One::f(); // this->One::f();
        std::cout << "Two." << std::endl;
    }
};

struct Three : Two {
    void f() override {
        Two::f(); // this->Two::f();
        std::cout << "Three." << std::endl;
    }
};

// ...

Three t;

t.f();      // Normal syntax.
t.Two::f(); // Calls version of f() defined in Two.
t.One::f(); // Calls version of f() defined in One.

仮想メンバー関数

メンバー関数をvirtualとして宣言することもできvirtualこの場合、ポインタまたはインスタンスへの参照で呼び出された場合、それらは直接アクセスされません。むしろ、仮想関数テーブル( vtableまたはvftableとしてよく知られている仮想関数のためのポインタ関数のメンバ関数のリスト)で関数をvftable 、それを使用してインスタンスの動的に適したバージョンを呼び出す(実際の)タイプです。関数がクラスの変数から直接呼び出された場合、ルックアップは実行されません。

struct Base {
    virtual void func() { std::cout << "In Base." << std::endl; }
};

struct Derived : Base {
    void func() override { std::cout << "In Derived." << std::endl; }
};

void slicer(Base x) { x.func(); }

// ...

Base b;
Derived d;

Base *pb = &b, *pd = &d; // Pointers.
Base &rb = b, &rd = d;   // References.

b.func();   // Output:  In Base.
d.func();   // Output:  In Derived.

pb->func(); // Output:  In Base.
pd->func(); // Output:  In Derived.

rb.func();  // Output:  In Base.
rd.func();  // Output:  In Derived.

slicer(b);  // Output:  In Base.
slicer(d);  // Output:  In Base.

なおながらpdあるBase* 、及びrdあるBase&呼び出すfunc()の二つの呼び出しのいずれかにDerived::func()の代わりに、 Base::func()これは、 DerivedvtableBase::func()エントリをDerived::func()指すように更新するためです。逆に、インスタンスをslicer()渡すと、渡されたインスタンスがDerivedであっても、 Base::func()が呼び出されることになります。これは、通過するデータスライスとして知られているものであるDerivedにインスタンスをBase値によりパラメータが部分描画DerivedないインスタンスBaseアクセスできないインスタンス。

メンバ関数が仮想として定義されている場合、オーバーライド関数がvirtualとして指定されているかどうかにかかわらず、同じシグネチャを持つすべての派生クラスメンバ関数が優先します。これは、プログラマが解析するために派生したクラスをより難しくすることができます。ただし、どの関数がvirtualあるかについての指示がないためです。

struct B {
    virtual void f() {}
};

struct D : B {
    void f() {} // Implicitly virtual, overrides B::f.
                //  You'd have to check B to know that, though.
};

ただし、派生関数は、その署名が一致する場合にのみ基本関数をオーバーライドすることに注意してください。導出関数が明示的にvirtual宣言されたとしても、署名が不一致であれば、代わりに新しい仮想関数を作成します。

struct BadB {
    virtual void f() {}
};

struct BadD : BadB {
    virtual void f(int i) {} // Does NOT override BadB::f.
};
C ++ 11

C ++ 11以降、上書きする意向は、状況依存のキーワードのoverride明示できoverride 。これは、プログラマが基本クラス関数をオーバーライドすることを期待していることをコンパイラに伝えます。コンパイラは何もオーバーライドしないとエラーを省略します。

struct CPP11B {
    virtual void f() {}
};

struct CPP11D : CPP11B {
    void f() override {}
    void f(int i) override {} // Error: Doesn't actually override anything.
};

これはまた、関数が仮想であり、少なくとも1つの基本クラスでも宣言されていることをプログラマーに伝えるメリットがあります。複雑なクラスを解析しやすくすることができます。

関数がvirtual宣言され、クラス定義の外で定義されるとき、 virtual指定子は関数宣言に含まれなければならず、定義内で繰り返されません。

C ++ 11

これはoverrideも当てはまりoverride

struct VB {
    virtual void f(); // "virtual" goes here.
    void g();
};
/* virtual */ void VB::f() {} // Not here.
virtual void VB::g() {} // Error.

基本クラスがvirtual関数をオーバーロードする場合、 virtualとして明示的に指定されたオーバーロードのみがvirtualになります。

struct BOverload {
    virtual void func() {}
    void func(int) {}
};

struct DOverload : BOverload {
    void func() override {}
    void func(int) {}
};

// ...

BOverload* bo = new DOverload;
bo->func(); // Calls DOverload::func().
bo->func(1); // Calls BOverload::func(int).

詳細については、該当するトピックを参照してください。

Const Correctness

this cv修飾子の主な用途の1つは、 const正しさです。これは、オブジェクトを変更する必要のあるアクセスのみがオブジェクトを変更できること 、およびオブジェクトを変更する必要のない(メンバまたは非メンバ)関数には、そのオブジェクトへの書き込みアクセス権がないことを保証する方法ですオブジェクト(直接的にも間接的にも)。これにより意図しない変更が防止され、コードのエラーが少なくなります。また、状態を変更する必要のない関数は、関数を書き換えたり、オーバーロードすることなく、 constまたは非constオブジェクトのいずれかを取ることができます。

const正しさは、性質上、ボトムアップから始まります。状態を変更する必要のないクラスメンバ関数は、 constインスタンスとしてconstようにconstとして宣言されています。これは順番に、渡された参照渡しパラメータを変更する必要がないときにconst宣言することを可能にします。これにより、関数はconstやnon- constオブジェクトのいずれかを不平を言わずに取ることができ、 const -nessはこの方法。このため、ゲッターは、論理状態を変更する必要のない他の関数と同様に、頻繁にconstです。

class ConstIncorrect {
    Field fld;

  public:
    ConstIncorrect(const Field& f) : fld(f) {}     // Modifies.

    const Field& get_field()       { return fld; } // Doesn't modify; should be const.
    void set_field(const Field& f) { fld = f; }    // Modifies.

    void do_something(int i) {                     // Modifies.
        fld.insert_value(i);
    }
    void do_nothing()        { }                   // Doesn't modify; should be const.
};

class ConstCorrect {
    Field fld;

  public:
    ConstCorrect(const Field& f) : fld(f) {}       // Not const: Modifies.

    const Field& get_field() const { return fld; } // const: Doesn't modify.
    void set_field(const Field& f) { fld = f; }    // Not const: Modifies.

    void do_something(int i) {                     // Not const: Modifies.
        fld.insert_value(i);
    }
    void do_nothing() const  { }                   // const: Doesn't modify.
};

// ...

const ConstIncorrect i_cant_do_anything(make_me_a_field());
// Now, let's read it...
Field f = i_cant_do_anything.get_field();
  // Error: Loses cv-qualifiers, get_field() isn't const.
i_cant_do_anything.do_nothing();
  // Error: Same as above.
// Oops.

const ConstCorrect but_i_can(make_me_a_field());
// Now, let's read it...
Field f = but_i_can.get_field(); // Good.
but_i_can.do_nothing();          // Good.

ConstIncorrectConstCorrectコメントで示されているように、適切なcv修飾関数もドキュメントとして機能します。クラスがある場合はconst正しい、ではありません任意の関数const安全状態を変更すると仮定することができ、ある任意の関数const安全な状態を変更しないと仮定することができます。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow