c++ 静态绑定和动态绑定

  C++ 中有两种不同的函数调用方式:静态绑定和动态绑定。

静态绑定

  静态绑定是指在编译时确定调用哪个函数。也就是说,编译器会根据函数调用的名称和参数类型来确定要调用的函数。这种方式也被称为静态多态或编译时多态。

静态绑定适用于以下情况:

  1. 在编译时已经知道调用哪个函数。
  2. 函数的名称和参数类型是确定的。
  3. 函数调用的目标是一个静态类型的对象。

例如:

class Shape {
public:
    void draw() {
        cout << "Drawing shape" << endl;
    }
};

class Circle : public Shape {
public:
    void draw() {
        cout << "Drawing circle" << endl;
    }
};

int main() {
    Circle c;
    Shape* s = &c;
    s->draw(); // 静态绑定,输出 Drawing shape
    return 0;
}

  上述代码中,调用s->draw()函数时,由于s的静态类型是Shape*,因此编译器会使用静态绑定来调用Shape::draw()函数,而不是Circle::draw()函数。

动态绑定

  动态绑定是指在运行时确定调用哪个函数。也就是说,编译器不确定要调用哪个函数,而是将函数调用委托给运行时系统来决定。这种方式也被称为动态多态或运行时多态。

动态绑定适用于以下情况:

  1. 在运行时才知道调用哪个函数。
  2. 函数的名称和参数类型不确定,只有在运行时才能确定。
  3. 函数调用的目标是一个动态类型的对象。

例如:

class Shape {
public:
    virtual void draw() {
        cout << "Drawing shape" << endl;
    }
};

class Circle : public Shape {
public:
    void draw() {
        cout << "Drawing circle" << endl;
    }
};

int main() {
    Circle c;
    Shape* s = &c;
    s->draw(); // 动态绑定,输出 Drawing circle
    return 0;
}

  上述代码中,调用s->draw()函数时,由于s的动态类型是Circle,因此运行时系统会使用动态绑定来调用Circle::draw()函数,而不是Shape::draw()函数。这就是C++中的虚函数机制,通过在函数前加上virtual关键字来实现。

空指针调用函数

#include 
using namespace std;

class test {
public:
    void show() { cout << "I'm test" << endl; }
};

int main()
{
    test* t = nullptr;
    t->show(); // 输出 I'm test
}

  上面的代码是可以正常运行的。

  在 c++ 中,类的成员函数并不与特定对象绑定,所有成员函数共用一份成员函数体,当程序编译后,成员函数的地址即已经确定。(静态绑定)

  当调用 t->show()时,实际上执行的代码是 test::show(this), 成员函数 show() 的地址在编译器就已经确定。test::show(this) 操作相当于执行了一个函数,传入的参数为 nullptr。而在 show() 函数中没有用到 this 指针,所以程序正常运行,不会报错。

如果把代码改成下面这样,程序就会 crash

#include 
using namespace std;

class test  {
public:
    void show() {
        a = 2;
    }

private:
    int a;
};

int main()
{
    test* t = nullptr;
    t->show();
}

在 show() 函数里会执行 this->a = 2;, 而 this 指针是 nullptr, 所以会报错。

所以,空指针也不能调用虚函数,因为虚函数的地址是动态绑定的,在运行时要通过虚函数表指针 this->vptr 找到对象对应的类的虚函数表(vtbl)。此时 this 为空,就会报错

你可能感兴趣的:(c++,虚函数,静态绑定)