【问题标题】:Cases of static and dynamic binding in C++C++中静态和动态绑定的案例
【发布时间】:2021-06-20 04:21:03
【问题描述】:

以下代码有 4 个类:Base1、Derived1(派生自 Base1)、Base2、Derived2(派生自 Base2)。两个基类都有整数 data1 和 display_data() 函数。两个派生类都有整数 data1 和 data2,以及 display_data() 函数。

我在我的代码中尝试了 4 种情况,可以在 main 函数中看到。我无法确定其中哪些是静态绑定的情况,哪些是动态的。我需要一些帮助。 我也想知道,这些情况中哪些可以被认为是“函数覆盖”。

代码

#include <iostream>
using namespace std;

class Base1{
protected:
    int data1;

public:
    Base1(int idata1 = 0) {
        data1 = idata1;
    }

    void display_data() {
        cout << "Base1: " << data1 << endl;
    }
};

class Derived1 : public Base1 {
protected:
    int data2;

public:
    Derived1(int idata1 = 0, int idata2 = 0) {
        data1 = idata1;
        data2 = idata2;
    }

    void display_data() {
        cout << "Derived1: " << data1 << ' ' << data2 << endl;
    }
};


class Base2 {
protected:
    int data1;

public:
    Base2(int idata1 = 0) {
        data1 = idata1;
    }

    virtual void display_data() {
        cout << "Base2: " << data1 << endl;
    }
};

class Derived2 : public Base2 {
protected:
    int data2;

public:
    Derived2(int idata1 = 0, int idata2 = 0) {
        data1 = idata1;
        data2 = idata2;
    }

    void display_data() {
        cout << "Derived2: " << data1 << ' ' << data2 << endl;
    }
};

int main()
{
    // case 1
    Derived1 d1(1, 10);
    d1.display_data();

    // case 2
    Base1* d2 = new Derived1(2, 20);
    d2->display_data();

    // case 3
    Derived2 d3(3, 30);
    d3.display_data();

    // case 4
    Base2* d4 = new Derived2(4, 40);
    d4->display_data();

    return 0;
}

输出

Derived1: 1 10
Base1: 2
Derived2: 3 30
Derived2: 4 40

【问题讨论】:

  • 这里的关键是virtual函数说明符。当成员函数不是virtual 时,使用的版本是在编译时根据调用该函数的对象的类型确定的——因此在情况2 中,您会看到Base1::display_data 被调用,因为它是从@ 调用的987654326@ 指针。当函数为virtual时,使用的版本是在运行时根据对象的实际最衍生类型确定的,见案例4。
  • 当您阅读“虚拟”时,请考虑“动态绑定和可覆盖”。

标签: c++ oop polymorphism dynamic-binding static-binding


【解决方案1】:

这是我尝试用简单的方式解释它:)

当对象与基于对象的静态类型(了解其类的类型)的成员函数相关联时,会发生静态绑定。

当指针或引用与基于对象的动态类型(在运行时了解变量的实例)的成员函数相关联时,就会发生动态绑定。

继续阅读之前:动态绑定仅适用于指针或引用以及基类的虚函数。

第一次调用是静态绑定(也称为早期绑定),因为调用函数所需的一切都是已知的 在编译时。

    Derived1 d1(1, 10);
    d1.display_data();

您知道d1 实例是Derived1 类型的自动变量,然后它会调用 方法Derived1::display_data()

  • 第一个条件不正确:d1 既不是指针也不是引用。
  • 第二个条件不行:Derived1::display_data 不是虚拟的。

第二次通话

    Base1* d2 = new Derived1(2, 20);
    d2->display_data();

我们看到变量d2的声明类型是Base1,但实例是Derived1(它 由于继承是正确的,因此Derived1Base1 类)。但你还不知道 如果它将调用的display_data 方法是来自Base1::display_data 的方法,或者是来自Base1::display_data 的方法 Derived1::display_data.

  • 第一个条件没问题,因为我们有类型指针Base1*d2
  • 第二个条件不正确,因为Base1::display_data 不是虚拟的。因此它仍然 一个静态绑定,那么将被调用的函数是具有声明类型的函数,因此 代码将调用Base1::display_data

第三次通话

    // case 3
    Derived2 d3(3, 30);
    d3.display_data();

这将导致静态绑定,然后调用Derived3::display_data

  • 第一个条件不正确:d3 既不是指针也不是引用。
  • 第二个条件OK:Derived2::display_data是虚拟的。

第四次通话

    Base2* d4 = new Derived2(4, 40);
    d4->display_data();

这次是动态绑定。

  • 第一个条件OK:d4是一个指针。
  • 第二个条件OK:Derived2::display_data是虚拟的。 因此,它不会从声明的类型base2 调用该方法,而是调用该方法 从运行时声明的实例Derived2::display_data

【讨论】:

  • 有什么实用的方法可以判断代码的某个部分是使用静态绑定还是动态绑定?诸如 ide 功能或一些打印语句之类的东西,可以用于出现的任何其他情况。还是我们必须按照您解释的方式理解它?另外,您能否澄清一下所有这些情况是否都被视为“方法覆盖”?由于在线提供的多种资源,我可能对其定义感到困惑。它与它是虚拟的有关吗?谢谢你这么详细的回答。这清除了静态和动态绑定的概念。 :)
  • 实用的方法是找到@molbdnilo 所说的关键字virtual。您可能想了解为什么 destructor 应该是 virtual 并更好地理解为什么方法也应该是虚拟的。实际上,动态绑定是通过虚拟方法的继承(覆盖)来利用的。方法覆盖是在派生类方法中重新定义基类方法但更改其定义的事实(正是您为派生类所做的)。你可能有一些简明的文献here
猜你喜欢
  • 2015-09-14
  • 1970-01-01
  • 2012-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多