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