无意中发现一个关于C++基础的问题,当时愣是没理解是什么原因,现在搞明白了,写下来了。先看小程序,先实践再理论吧,要不大家睡着了。

    #include <iostream>
    using namespace std;
    class Base
    {
    public:
          virtual void funtion(int arg = 1){cout<<arg<<endl;}      
    };
    class Derive : public Base
    {
    public:
          virtual void funtion(){cout<<"Derive"<<endl;}
          virtual void funtion(int arg){cout<<"Derive"<<arg<<endl;}      
    };
    int main(int argc, char *argv[])
    {
        Base* obj = new Derive();
        obj->funtion();
        system("pause");
        return 0;    
    }

  上面的程序会出现什么结果呢?我想会有很多人看到这个地方会怀疑我程序的正确性了,大呼“你的程序是错的”,但真的错吗?我们可以先执行下程序看下结果。很明显,结果是调用了子类的函数,并且子类中arg参数的值是父类中的值1,运行结果为“Derive 1”。

  下面我解释下这种结果的原因,首先我先要说下对象的两种类型:动态类型和静态类型。

  静态类型:指针或者是引用声明时的类型。

  动态类型:由他实际指向的类型确定。

  例如:

    Base *pgo=   //pgo静态类型是Base *
    new Derive; //动态类型是Derive *
    Asterioid *pa = new Asterioid; //pa的静态类型是 Asterioid *
    //动态类型也是 Asterioid *
    pgo = pa; //pgo静态类型总指向Base *
    //动态类型指向了 Asterioid *
    Base &rgo = *pa; //rgo的静态类型是Base
    //动态类型是 Asterioid

  虚函数是动态绑定的,而默认参数值是静态绑定的。运行时效率。如果默认参数值是动态绑定的话,那么编译器必须提供一整套方案,为运行时的虚函数参数确定恰当的默认值。而这样做,比起C++当前使用的编译时决定机制而言,将会更复杂、更慢。鱼和熊掌不可兼得,C++将设计的中心倾向了速度和简洁,你在享受效率的快感的同时,如果你忽略本条目的建议,你会陷入困惑。

  其实对于这个问题在[Effective C++第3版]中也有提到,其第36条:避免对派生的非虚函数进行重定义。下面看下书中的描述:

  现在考虑以下的层次结构:B是一个基类,D是由B的公有继承类,B类中定义了一个公有成员函数mf,由于这里mf的参数和返回值不是讨论的重点,因此假设mf是无参数无返回值的函数。也是说:

    class B {
    public:
      void mf();
    };
    class D: public B {  };
    即使不知道B、D、mf的任何信息,让我们声明一个D的对象x:
    D x;                           // x 是D类型的对象
    B *pB = &x;                       // 指向x的指针
    pB->mf();                         // 通过指针调用mf函数
    D *pD = &x;                       // 指向x的指针
    pD->mf();                         // 通过指针调用mf函数

  在这里,如果告诉你pD->mf()与pB->mf()可能拥有不同的行为,你一定会感到意外。这也难怪:因为两次都是在调用x对象的成员函数mf,因为两种情况下都是用了同一函数和同一对象,mf()理所应当应该有一致的行为。难道不是吗?