静态动态绑定

2021-09-04  本文已影响0人  404Not_Found

静态与动态绑定

//静态与d动态
class Base
{
public:
};

class Derive :public Base
{
public:
};

class Derive2 :public Base
{
public:
};


int main(int argc, char ** argv)
{
    //静态类型, 对象定义的类型,编译期间是能确定好的,定义的是什么,就是什么
    Base base;// 定义的类型就是Base
    Derive derive;
    Base * pBase;
    Base *pbase2 = new Derive(); //类型也是确定的 是Base *
    Base *pbase3 = new Derive2(); //类型也是确定的 是Base *

    //动态类型,只有指针和引用才有此类做法,运行的时候才有
    //pBase 类型 飘忽不变
    pBase = pbase2;
    pBase = pbase3;


    return 0;
}

静态类型:编译期间就已经定了。可以理解为执行文件中就已经写死了。即使像也是静态绑定

Base *pbase2 = new Derive();

动态绑定,类型飘忽不定

函数的动静态绑定

普通成员函数

相比于上个例子中的代码,子类与父类增加了相同的成员函数

class Base
{
public:
    void myfunc()
    {
        cout << "Base::myfun()" << endl;
    }
};

class Derive :public Base
{
public:
    void myfunc()
    {
        cout << "Derive::myfun()" << endl;
    }
};

class Derive2 :public Base
{
public:
    void myfunc()
    {
        cout << "Derive2::myfun()" << endl;
    }
};


int main(int argc, char ** argv)
{

    Derive derive;
    Derive* pderive = &derive;
    pderive->myfunc();//Derive::myfunc
    
    Base*pbase = &derive;
    pbase->myfunc();//Base::myfunc
    
    return 0;
}

代码规范: 不能在子类中重新定义一个基类中的普通成员函数

虚函数

增加虚函数代码

class Base
{
public:
    void myfunc()
    {
        cout << "Base::myfun()" << endl;
    }
    virtual void myVirFunc() {
        cout << "Base:: myVirFunc()" << endl;
    }
};

class Derive :public Base
{
public:
    void myfunc()
    {
        cout << "Derive::myfun()" << endl;
    }
    virtual void myVirFunc() {
        cout << "Derive:: myVirFunc()" << endl;
    }
};

class Derive2 :public Base
{
public:
    void myfunc()
    {
        cout << "Derive2::myfun()" << endl;
    }

    virtual void myVirFunc() {
        cout << "Derive2:: myVirFunc()" << endl;
    }
};


int main(int argc, char ** argv)
{

    Derive derive;
    Derive* pderive = &derive;
    
    Base*pbase = &derive;
    Base base;
    pderive->myVirFunc();//Derive
    pbase->myVirFunc();//Derive

    pbase = &base;
    pbase->myVirFunc(); //Base


    return 0;
}

非常明显的动态绑定。

虚函数中默认参数的坑

class Base
{
public:
    void myfunc()
    {
        cout << "Base::myfun()" << endl;
    }
    virtual void myVirFunc(int value =1) {
        cout << "Base:: myVirFunc(), value = " << value << endl;
    }
};

class Derive :public Base
{
public:
    void myfunc()
    {
        cout << "Derive::myfun()" << endl;
    }
    virtual void myVirFunc(int value =2) {
        cout << "Derive:: myVirFunc(), value= " << value << endl;
    }
};

int main(int argc, char ** argv)
{
    Derive derive;
    Derive* pderive = &derive;
    
    Base*pbase = &derive;
    Base base;
    pderive->myVirFunc();//Derive default value = 2
    pbase->myVirFunc();//Derive default value = 1;

    pbase = &base;
    pbase->myVirFunc(); //Base default value= 1;
    return 0;
}

虚函数的默认参数是静态绑定的。生命的类型是谁,就用谁的虚函数默认参数。

谈多态

  1. 父类有虚函数,子类必有虚函数表,子类重写父类的虚函数
  2. 父类指针 or 引用 指向子类地址 or 子类对象
  3. 父类指针 or 引用 调用子类中重写的虚函数时,能看到多态,调用的实际是子类的虚函数。

多态注定要依靠虚函数的

class A
{
public:
    virtual void myvirfun()
    {
        printf("A:myvirfun()");
    }
};

class B:public A
{
public:
};

int main(int argc, char ** argv)
{

    A *pa = new A();
    pa->myvirfun();//多态

    A a;
    //进入反汇编 就2行代码
    a.myvirfun();//非多态

    A*ppa = &a;
    ppa->myvirfun();//多态

    B b;
    b.myvirfun();

    return 0;
}

注意:

A a;
a.myvirfun();

非多态,进入反汇编,则只能看到2行代码 调用的是 A::myvirfun();

虚函数的调用

小测试 虚函数位置

问: 虚函数i() 在虚函数表什么位置

class Base
{
public:
    virtual void f()
    {
        cout << "Base::f()" << endl;
    }
    virtual void g()
    {
        cout << "Base::g()" << endl;
    }
    virtual void h()
    {
        cout << "Base::h()" << endl;
    }
};

class Derive :public Base
{
public:
    virtual void i()
    {
        cout << "Derive::self i()" << endl;
    }
    virtual void g()
    {
        cout << "Derive::g()" << endl;
    }
    void myselffunc(){}
};

int main(int argc, char**argv)
{
    //一 单继承下的虚函数测试
    //由反汇编测试可知,i 在子类虚函数表的最后。看图即可。
    Derive myDerive;
    Derive *pMyDerive = &myDerive;
    pMyDerive->f();
    pMyDerive->g();
    pMyDerive->h();
    pMyDerive->i();

    return 0;
}

打断点,在反汇编中可以观察到,子类自己的虚函数i()在虚函数表的最后

虚函数调用代码(编译器视角)

class Base
{
public:
    virtual void f()
    {
        cout << "Base::f()" << endl;
    }
    virtual void g()
    {
        cout << "Base::g()" << endl;
    }
    virtual void h()
    {
        cout << "Base::h()" << endl;
    }
};

class Derive :public Base
{
public:
    virtual void i()
    {
        cout << "Derive::self i()" << endl;
    }
    virtual void g()
    {
        cout << "Derive::g()" << endl;
    }
    void myselffunc(){}
};

int main(int argc, char**argv)
{
    Base *pb = new Derive();
    pb->g();
    /*
    (*pb->vptr[1])(pb); 1.传入this指针,2.虚函数表中每个虚函数的位置已经定死啦。    
    */
    //我们需要知道的是运行期间,pb调用哪个虚函数表

    Derive myderive;
    Base &mb = myderive;
    mb.g();
    return 0;
}
  1. 虚函数表中,每隔虚函数的位置都已经写死在可执行文件中
  2. 继承中,确认 pb 调用哪个虚函数表是关键(多继承中会用到)

vptr 的生成

vptr 编译时候生成,编译器的在构造函数中插入了给vptr 的赋值代码
所以构造函数不可以为虚函数,构造函数中就是要构造虚函数表的。把构造函数搞成虚函数毫无意义的。
子类中如果没有任何虚函数,不会复用父类的虚函数表,还会另起炉灶,弄一份一模一样的虚函数表

上一篇 下一篇

猜你喜欢

热点阅读