重新定义继承而来的缺省参数值导致的问题

来源:互联网 发布:923字刷爆朋友圈 知乎 编辑:程序博客网 时间:2024/06/05 19:47

不要重新定义继承而来的缺省参数值,这样会导致与你预期的行为不同,《Effective c++》一书中的条款37也对该问题有所讲述。

那么会出现什么问题呢?看如下代码:

#include <iostream>class Base{public:    virtual void Show(int n = 10)const{    //提供缺省参数值        std::cout << "Base:" << n << std::endl;    }};class Base1 : public Base{public:    virtual void Show(int n)const{        std::cout << "Base1:" << n << std::endl;    }};int main(){    Base* p1 = new Base1;    p1->Show(5);           //#1    p1->Show();            //#2    return 0;}
输出:

Base1:5Base1:10
#1 语句 因多态特性 调用的是Base1::Show  将5作为参数 输出Base1:5 没问题
#2 语句 没有提供实参 则使用从基类继承而来的缺省参数值10 输出Base1:10 也没问题

但是当派生类继承了Show函数后也重新定义了缺省参数值 将会导致问题,看如下代码:

#include <iostream>class Base{public:    virtual void Show(int n = 10)const{    //提供缺省参数值        std::cout << "Base:" << n << std::endl;    }};class Base1 : public Base{public:    virtual void Show(int n = 20)const{     //重新定义继承而来的缺省参数值        std::cout << "Base1:" << n << std::endl;    }};int main(){    Base* p1 = new Base1;    p1->Show(5);           //#1    p1->Show();            //#2    return 0;}
到了这里 可能会有同学说 这跟上面差不多啊 #1语句还是输出Base1:5 #2语句因没有提供实参则使用重新定义的缺省参数值啊(Base1:20)但是实际上输出结果是:

Base1:5Base1:10
这里 可能就有同学疑惑了,不应该用重新定义的缺省值吗?为什么还是用的基类提供的缺省值?这是因为缺省参数值是静态绑定的 而虚函数是动态绑定的。

上面代码中Base* p1 = new Base1;中p1是指向Base的指针 是静态类型,动态类型就是Base1*。

而虚函数是通过动态绑定而来,意思是调用哪个版本的虚函数,取决于发出调用的那个对象的动态类型,这里动态类型是Base1*,所以调用该类版本的虚函数,但是缺省参数值是静态绑定,也就是当没有提供参数的时候,使用哪个虚函数版本提供的缺省参数值是由静态类型决定的 ,这里也就是Base版本的提供的缺省值。所以#2使用的是Base版本的缺省值而不是Base1的版本的缺省值。

到这里 可能还是有同学不是很明白,不要紧,那么再看如下代码,可能会清楚很多。

#include <iostream>class Base{public:    virtual void Show(int n = 10)const{         //提供缺省参数值        std::cout << "Base:" << n << std::endl;    }};class Base1 : public Base{public:    virtual void Show(int n = 20)const{         //重新定义缺省值        std::cout << "Base1:" << n << std::endl;    }};class Base2 : public Base1{public:    virtual void Show(int n = 50)const{         //重新定义缺省值        std::cout << "Base2:" << n << std::endl;    }};int main(){    Base* p1 = new Base1;    p1->Show(5);                   //#1    p1->Show();                    //#2    Base* p2 = new Base2;          //静态类型p2(Base*) 动态类型(Base2*)    p2->Show();                    //#3    Base1* p3 = new Base1;         //静态类型p3(Base1*) 动态类型(Base1*)    p3->Show();                    //#4    Base1* p4 = new Base2;         //静态类型p4(Base1*) 动态类型(Base2*)    p4->Show();                    //#5    Base2* p5 = new Base2;         //静态类型p5(Base2*) 动态类型(Base2)    p5->Show();                    //#6    return 0;}
以上代码有一个继承体系:Base)Base1)Base2

#3调用动态类型版本的虚函数Base2::Show,然后使用静态类型提供的缺省值(10)

#4 #5 #6 都是一样的 调用动态类型版本的虚函数 然后使用静态类型提供的缺省值

所以最终输出结果:

Base1:5Base1:10Base2:10Base1:20Base2:20Base2:50
另外不仅仅是通过指针调用会出现这种问题 通过引用调用也会有这种问题 所以不要重新定义继承而来的缺省参数值 因为这样不会有你所希望的执行结果。有人可能会问了 通过对象而不是指针或引用调用“虚函数”不会出现如上问题啊。是啊,的确不会出现如上问题,但是你通过对象调用的话,就同时失去了多态性,因为对象调用,是直接调用的该对象版本的“虚函数” 这样虚函数就没有什么意义了。既然定义虚函数,就可能会使用多态特性,使用多态特性就可能会出现如上问题。


阅读全文
0 0