30

次のコード フラグメントを考えると、関数呼び出しの違いは何ですか? 関数隠蔽とは?関数のオーバーライドとは それらは関数のオーバーロードとどのように関係していますか? 2つの違いは何ですか?これらについての適切な説明を 1 か所で見つけることができなかったので、情報を統合できるようにここで質問しています。

class Parent {
  public:
    void doA() { cout << "doA in Parent" << endl; }
    virtual void doB() { cout << "doB in Parent" << endl; }
};

class Child : public Parent {
  public:
    void doA() { cout << "doA in Child" << endl; }
    void doB() { cout << "doB in Child" << endl; }
};

Parent* p1 = new Parent();
Parent* p2 = new Child();
Child* cp = new Child();

void testStuff() {
  p1->doA();
  p2->doA();
  cp->doA();

  p1->doB();
  p2->doB();
  cp->doB();
}
4

5 に答える 5

7

仮想メンバー関数の呼び出しと仮想メンバー関数の呼び出しの違いは、定義上、前者の場合、呼び出しで使用されるオブジェクト式の動的な型に従ってターゲット関数が選択されるのに対し、後者の場合は、ターゲット関数が選択されることです。静的タイプが使用される場合。

それだけです。あなたの例はp2->doA()p2->doB()呼び出しによるこの違いを明確に示しています。式の静的型は*p2ですがParent、同じ式の動的型は ですChildp2->doA()これが、呼び出しParent::doAp2->doB()呼び出しの理由Child::doBです。

その違いが重要な状況では、名前の隠蔽はまったく関係ありません。

于 2015-06-27T22:21:01.250 に答える
3

簡単なものから始めましょう。

p1Parentポインターであるため、常にParentのメンバー関数を呼び出します。

cpは へのポインタであるため、常にのメンバー関数Childを呼び出します。Child

今はもっと難しいものです。p2Parentポインタですが、タイプ のオブジェクトを指しているため、一致する関数が仮想であるか、関数が 内にのみ存在し、 内に存在しない場合はいつでも の関数Childを呼び出します。つまり、は独自ので非表示になりますが、 をオーバーライドします。関数の隠蔽は、関数のオーバーロードの一種と見なされることがあります。これは、同じ名前の関数に別の実装が与えられるためです。非表示関数は非表示関数とは異なるクラスにあるため、シグネチャが異なり、どちらを使用するかが明確になります。ChildParentChildParentChildParent::doA()doA()Parent::doB()

の出力は次のようにtestStuff()なります

doA in Parent
doA in Parent
doA in Child
doB in Parent
doB in Child
doB in Child

いずれにせよ、Parent::doA()関数の「仮想性」に関係なく、名前解決を使用してParent::doB()呼び出すことができます。Child関数

void Child::doX() {
  doA();
  doB();
  Parent::doA();
  Parent::doB();
  cout << "doX in Child" << endl;
}

cp->doX()出力によって呼び出されたときにこれを示します

doA in Child
doB in Child
doA in Parent
doB in Parent
doX in Child

さらに、のバージョンのcp->Parent::doA()を呼び出します。ParentdoA()

p2doX()はであるため参照できません。Parent*また、Parentについて何も知りませんChild。ただし、は として初期化p2されているため、 にキャストできます。Child*その後、 を呼び出すために使用できますdoX()

于 2013-11-01T22:15:57.507 に答える
2

それらすべての白黒が異なる、はるかに簡単な例です。

class Base {
public:
    virtual int fcn();
};

class D1 : public Base {
public:  
    // D1 inherits the definition of Base::fcn()
    int fcn(int);  // parameter list differs from fcn in Base
    virtual void f2(); // new virtual function that does not exist in Base
};

class D2 : public D1 {
public:
    int fcn(int); // nonvirtual function hides D1::fcn(int)
    int fcn();  // overrides virtual fcn from Base
    void f2();  // overrides virtual f2 from D1
}
于 2015-03-31T18:35:19.043 に答える
1

質問に書かれているサンプルコードは、実行すると基本的に答えが得られます。

非仮想関数を呼び出すと、オブジェクトが実際に他の派生型として作成されたかどうかに関係なく、ポインター型と同じクラスの関数が使用されます。仮想関数を呼び出すと、使用しているポインターの種類に関係なく、元の割り当てられたオブジェクト型の関数が使用されます。

したがって、この場合のプログラムの出力は次のようになります。

doA in Parent
doA in Parent
doA in Child
doB in Parent
doB in Child
doB in Child
于 2013-11-01T23:30:11.493 に答える