• C++ 静态绑定与动态绑定------绝不重新定义继承而来的缺省参数


            在了解静态绑定和动态绑定之前,先了解什么是对象的静态类型,什么是对象的动态类型。

    • 对象的静态类型:对象在声明时采用的类型。是在编译器决定的。
    • 对象的动态类型:目前所指对象的类型。是在运行期决定的。
    动态类型可以更改,而静态类型不可更改。看一个示例
    1. class Base
    2. {
    3. public:
    4. void setData(int i=10)
    5. {
    6. cout <<" virtual int Base::setData()"<<endl;
    7. }
    8. virtual int getData()
    9. {
    10. cout <<" virtual int Base::getData()"<<endl;
    11. }
    12. private:
    13. int m_value;
    14. };
    15. class Derive: public Base
    16. {
    17. public:
    18. void setData(int i=20)
    19. {
    20. cout <<" virtual int Derive::setData()"<<endl;
    21. }
    22. virtual int getData()
    23. {
    24. cout <<" virtual int Derive::getData()"<<endl;
    25. }
    26. };
    27. int _tmain(int argc, _TCHAR* argv[])
    28. {
    29. Derive *pd = new Derive;//pd的静态类型为Derive,动态类型也为Derive
    30. Base *pb = pd; //pb的静态类型为Base,动态类型为Derive
    31. return 0;
    32. }
    搞清楚了什么是对象的静态类型,什么是对象的动态类型。
    下面来介绍下什么是静态绑定,什么是动态绑定。

    • 静态绑定:绑定的对象是静态类型。某特性依赖于对象的静态类型,发生在编译期。
    • 动态绑定:绑定的对象是动态类型。有特性依赖于对象的动态类型,发生在运行期。
    只有虚函数才使用的是动态绑定,其他的全部是静态绑定。

    我们用pb,pd分别调用非虚函数,
    1. Derive *pd = new Derive;
    2. Base *pb = pd;
    3. pb->setData();
    4. pd->setData();
    输出:

    因为setData是静态绑定的,也就是编译器会在编译期根据对象的静态类型来选择函数。    pb的静态类型是Base,pd的静态类型是Derive。

    我们用pd,pb分别调用虚函数

    因为getData是虚函数,所以是动态绑定的。动态类型都是Derive*。
    分别调用了基类的函数和派生类的函数。注:这样的设计特别不好,派生类与基类之间发生了名称遮掩。只是为了静态绑定和动态绑定才这么写的。如果派生类和基类是is-a关系,任何情况下都不要继承一个non-virtual函数。

    注:指针和引用的动态类型与静态类型可能会不一致,但是对象的静态类型与动态类型是一致的。Derive d. d.setData()  d.getData()调用的都是派生类的成员函数。

    当虚函数有缺省参数的时候,情况变得有点复杂。因为缺省参数采用的是静态绑定。
    1. class Base
    2. {
    3. public:
    4. virtual void getData(int i=10)
    5. {
    6. cout <<" virtual int Base::getData()" << i <<endl;
    7. }
    8. };
    9. class Derive: public Base
    10. {
    11. public:
    12. virtual void getData(int i = 20)
    13. {
    14. cout <<" virtual int Derive::getData()" << i <<endl;
    15. }
    16. };
    17. int _tmain(int argc, _TCHAR* argv[])
    18. {
    19. Derive *pd = new Derive;
    20. Base *pb = pd;
    21. pb->getData();
    22. pd->getData();
    23. return 0;
    24. }
    输出:

    虽然调用的都是缺省参数,由于缺省参数采用的是静态绑定。因此才会得到这样的结果。为了防止这样的情况出现:effective c++专门指出了一条:绝不重新定义继承而来的缺省参数。为了避免这样的结果,有一种方法就是使用NVI(non-virtual interface) :在基类中声明一个共有的非虚函数并给出缺省参数(因为是静态绑定),在其中调用私有的虚函数,这样在调用派生类方法时。由于共有非虚函数采取的是静态绑定,且派生类肯定继承了非虚函数。因此调用该函数时,默认的参数即可一直。




  • 相关阅读:
    System.Configuration引用后ConfigurationManager方法用不了
    HTTP 错误 500.23
    slide ——首尾相接の平滑切换效果
    函数式编程初探之回调
    Call & Apply. It's easy!
    【W3C】 CSS3选择器
    再谈原型和原型链
    ECMA学习小结(3)——constructor 和 prototype
    ECMA学习小结(2)——一切皆对象
    ECMA学习小结(1)——什么是ECMAScript,JAVAScript与ECMA的关系
  • 原文地址:https://www.cnblogs.com/chengkeke/p/5417349.html
Copyright © 2020-2023  润新知