2011-06-24 10 views
32

私は次のコードに奇妙な状況があります。それを明確にするために私を助けてください。仮想関数のデフォルトの引数の振る舞い

class B 
{ 
     public: 
      B(); 
      virtual void print(int data=10) 
      { 
        cout << endl << "B--data=" << data; 
      } 
}; 
class D:public B 
{ 
     public: 
      D(); 
      void print(int data=20) 
      { 
        cout << endl << "D--data=" << data; 
      } 
}; 

int main() 
{ 
    B *bp = new D(); 
    bp->print(); 
return 0; 
} 

私は

[ D--data=20 ] 

を期待しかし、実用的で、それは

[ D--data=10 ] 

で出力について助けてください。それはあなたのために明らかに見えるかもしれませんが、私は内部メカニズムを認識していません。

+3

答えはあなたの問題を解決(またはあなたがそれを理解します)場合は、答えの左側にある緑色のチェックマークを使用して、それを受け入れてください。 –

答えて

22

デフォルトの引数は完全にコンパイル時の機能です。私。欠落している引数の代わりにデフォルトの引数を置き換えることは、コンパイル時に実行されます。この理由から、明らかに、メンバ関数のデフォルトの引数選択は、動的型(つまり実行時)型に依存することはありません。常にのスタティック(コンパイル時)のオブジェクトに依存します。

コードサンプルで書いた呼び出しは、他のものにかかわらず、即座にコンパイラによってbp->print(10)と解釈されます。

+0

私の疑問を解決してくれたAndreyに感謝します。私はこの方向には考えなかった。どうもありがとう。 –

30

標準は(8.3.6.10)言う:

仮想関数呼び出し(10.3) ポインタの静的タイプによって決定された仮想関数 の宣言で デフォルト引数を使用するか、または参照は オブジェクトを示します。 派生クラスのオーバーライド関数は、 オーバーライド関数からデフォルトの 引数を取得しません。

これは、あなたがタイプBのポインタを通じてprintを呼び出しているので、B::printのデフォルトの引数を使用して、意味します。

+0

Fundoo答えが理解するのに役立ちます。ありがとう –

0

あなたの変数はB型なので、Bの関数が呼び出されます。 Dを呼び出すには、変数をDとして宣言するか、Dにキャストする必要があります。

+0

'print'メンバ関数が仮想宣言されているので、ベースクラスポインタを呼び出すと、ベースバージョンではなくメンバ関数の派生バージョンが呼び出されます。 – templatetypedef

1

デフォルトの引数値は、呼び出し元に代わって渡されます。呼び出し側の観点からは、クラスB(Dではない)で動作するので、10(クラスBの場合)を返します。

3

一般に、これらのデフォルト引数は特定のスコープで表示されます。

#include <iostream> 
void frob (int x) { 
    std::cout << "frob(" << x << ")\n"; 
} 

void frob (int = 0); 
int main() { 
    frob();      // using 0 
    { 
     void frob (int x=5) ; 
     frob();     // using 5 
    } 
    { 
     void frob (int x=-5) ; 
     frob();     // using -5 
    } 
} 

あなたのケースでは、基本クラスの署名が表示されます(ただし、そうすべきではありません)。派生した既定の引数を使用するには、派生クラスへのポインタを介してその関数を明示的に呼び出すか、そのように宣言するか、正しくキャストする必要があります。

0

動的バインディングはvpointerとvtableを使用しています。ただし、動的バインディングは関数ポインタにのみ適用されます。動的バインド引数のメカニズムはありません。

したがって、デフォルトの引数はコンパイラ時に静的に決定されます。この場合、Baseクラスへのポインタであるbp型によって静的に決定されます。したがって、data = 10は関数引数として渡され、関数ポインタはDerivedクラスメンバ関数:D :: printを指しています。基本的には、D :: print(10)を呼び出します。

次のコードスニペットと結果の出力は、派生呼び出しメンバ関数Derived :: resize(int)を呼び出したにもかかわらず、Baseクラスのデフォルト引数:size = 0を渡します。

仮想空派生::リサイズ(int型)サイズ0

#include <iostream> 
#include <stdio.h> 
using namespace std; 

#define pr_dbgc(fmt,args...) \ 
    printf("%d %s " fmt "\n",__LINE__,__PRETTY_FUNCTION__, ##args); 

class Base { 
    public: 
     virtual void resize(int size=0){ 
      pr_dbgc("size %d",size); 
     } 
}; 

class Derived : public Base { 
    public: 
     void resize(int size=3){ 
      pr_dbgc("size %d",size); 
     } 
}; 

int main() 
{ 
    Base * base_p = new Base; 
    Derived * derived_p = new Derived; 

    base_p->resize();   /* calling base member function 
            resize with default 
            argument value --- size 0 */ 
    derived_p->resize();  /* calling derived member  
            function resize with default 
            argument default --- size 3 */ 

    base_p = derived_p;   /* dynamic binding using vpointer 
            and vtable */ 
           /* however, this dynamic binding only 
            applied to function pointer. 
            There is no mechanism to dynamic 
            binding argument. */ 
           /* So, the default argument is determined 
            statically by base_p type, 
            which is pointer to base class. Thus 
            size = 0 is passed as function 
            argument */ 

    base_p->resize();   /* polymorphism: calling derived class 
            member function 
            however with base member function 
            default value 0 --- size 0 */ 

    return 0; 
} 


#if 0 
The following shows the outputs: 
17 virtual void Base::resize(int) size 0 
24 virtual void Derived::resize(int) size 3 
24 virtual void Derived::resize(int) size 0 
#endif