2011-06-24 120 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

謝謝安德烈澄清我的懷疑。我沒有想到這個方向。非常感謝。 –

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。但是,動態綁定僅適用於函數指針。沒有動態綁定參數的機制。

因此,默認參數是在編譯器時間靜態確定的。在這種情況下,它是由bp類型靜態確定的,這是一個指向Base類的指針。因此data = 10作爲函數參數傳遞,而函數指針指向派生類成員函數:D :: print。本質上,它調用D :: print(10)。

以下代碼片段和結果輸出清楚地表明瞭一點:即使它調用Derived call成員函數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