探索C++对象模型

相关继承概念请参考:>继承相关概念<

虚函数&多态

  • 虚函数 — 类的成员函数前面加virtual关键字,则这个成员函数称为虚函数。
  • 虚函数重写 — 当在子类的定义了一个与父类完全相同的虚函数时,则称子类的这个函数重写(也称覆盖)了父类的这个虚函数。

多态的实现机制:

  C++中虚函数的主要作用就是实现多态。简单说父类的指针/引用调用重写的虚函数,当父类指针/引用指向父类对象时调用的是父类的虚函数,指向子类对象时调用的是子类的虚函数。
  这是通过虚函数表实现的,虚函数表通过一段连续的内存存放虚函数的地址,解决了虚函数重写(地址覆盖)问题。在有虚函数的对象实例中都有一张虚函数表,虚函数表就像一张地图,指明了具体调用哪个虚函数。

总结:

  1. 派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
  2. 基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
  3. 只有类的成员函数才能定义为虚函数。
  4. 静态成员函数不能定义为虚函数。
  5. 如果在类外定义虚函数,只能在声明函数时加virtual,类外定义函数时不能加virtual。
  6. 构造函数不能为虚函数,虽然可以将operator=定义为虚函数,但是最好不要将operator=定义为虚函数,因为容易使用时容易引起混淆。
  7. 不要在构造函数和析构函数里面调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会发生未定义的行为。
  8. 最好把基类的析构函数声明为虚函数。(因为派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里是因为编译器做了特殊处理)

多态

 多态就是多种形态,C++的多态分为静态多态和动态多态。

  1. 静态多态就是重载,因为是在编译期确定,所以称为静态多态。

  2. 动态多态就是通过继承重写基类的虚函数实现的多态,因为是在运行时确定,所以称为动态多态。

静态联编与动态联编:

#include <iostream>
#include <stdio.h>

using namespace std;

class Base
{
public:
    virtual void func1()
    {
        cout << "Base::func1()" << endl;
    }
    virtual void func2()
    {
        cout << "Base::func2()" << endl;
    }
    void func3()
    {
        cout << "Base::func3()" << endl;
    }
    void func4()
    {
        cout << "Base::func4()" << endl;
    }
protected:
    int _b;
};

class Derive : public Base
{
public:
    virtual void func1()
    {
        cout << "Derive::func1()" << endl;
    }
protected:
    int _d;
};

void func(Base& b)
{

    b.func1();
    b.func2();
    b.func3(); 
    b.func4();
}

int main()
{
    Base b;
    Derive d;
    func(b);
    func(d);

    return 0;
}

描述

单继承对象模型

#include <iostream>
#include <stdio.h>

using namespace std;

class Base
{
public:
    virtual void func1()
    {
        cout << "Base::func1()" << endl;
    }
    virtual void func2()
    {
        cout << "Base::func2()" << endl;
    }
private:
    int _a;
};

class Derive : public Base
{
    virtual void func1()
    {
        cout << "Derive::func1()" << endl;
    }
    virtual void func3()
    {
        cout << "Derive::func3()" << endl;
    }
    void func4()
    {
        cout << "Derive::func4()" << endl;
    }
private:
    int _b;
};
typedef void(*FUNC)();

void PrintVTable(int* table)
{
    printf("table:0x%p\n", table);
    for (int i = 0; table[i] != NULL; ++i)
    {
        printf("[%d]:0x%p -> ", i, table[i]);
        FUNC f = (FUNC)table[i];
        f();
    }
    cout << endl;
}

int main()
{
    Base b;
    Derive d;
    PrintVTable((int*)(*((int*)&b)));
    PrintVTable((int*)(*((int*)&d)));
    return 0;
}

描述

  由于func1()函数是虚函数,并且在Derive中被重写了,所以可以看到d对象中Derive的func1()函数覆盖了Base中的func1()函数,func2()直接继承。
  在Derive中首次声明func3()为虚函数,编译器不会再建立一张虚函数表,而是直接将这个虚函数地址写入到第一张虚函数表(Base)后面,并以0(NULL)结尾。此处并未将func3()在虚表中的地址打印出来是因为编译器的原因,我们可以手动将其打印出来。这里的_vfptr是虚函数表指针。

描述

描述

多重继承的对象模型

#include <iostream>
#include <stdio.h>

using namespace std;
class Base1
{
public:
    virtual void func1()
    {
        cout << "Base1::func1()" << endl;
    }
    virtual void func2()
    {
        cout << "Base1::func2()" << endl;
    }
private:
    int _b1;
};

class Base2
{
public:
    virtual void func1()
    {
        cout << "Base2::func1()" << endl;
    }
    virtual void func2()
    {
        cout << "Base2::func2()" << endl;
    }
private:
    int _b2;
};

class Derive : public Base1,public Base2
{
    virtual void func1()
    {
        cout << "Derive::func1()" << endl;
    }
    virtual void func3()
    {
        cout << "Derive::func3()" << endl;
    }
    void func4()
    {
        cout << "Derive::func4()" << endl;
    }
private:
    int _d;
};

typedef void(*FUNC)();

void PrintVTable(int* table)
{
    printf("table:0x%p\n", table);
    for (int i = 0; table[i] != NULL; ++i)
    {
        printf("[%d]:0x%p -> ", i, table[i]);
        FUNC f = (FUNC)table[i];
        f();
    }
    cout << endl;
}

int main()
{
    Base1 b1;
    Base2 b2;
    Derive d1;
    PrintVTable((int*)(*((int*)&b1)));
    PrintVTable((int*)(*((int*)&b2)));
    PrintVTable((int*)(*((int*)&d1)));
    PrintVTable((int*)(*((int*)((char*)&d1 + sizeof(Base2)))));   //打印第二张虚表
    return 0;
}

描述

Derive同时继承Base1和Base2。

描述

  在多重继承的对象模型中,先是Base1的虚表指针,再是Base1的成员变量,再是Base2的虚表指针,再是Base2的成员变量,最后是Derive的成员变量 。在Derive中首次声明func3()为虚函数,并不会再建立一张虚函数表,而是直接将这个虚函数地址写入到第一张虚函数表(Base1)后面,并以0(NULL)结尾。

描述

菱形继承的对象模型

#include <iostream>
#include <stdio.h>

using namespace std;

class A
{
public:
    virtual void f1()
    {
        cout << "A::f1()" << endl;
    }
public:
    int _a;
};

class B : public A
{
public:
    virtual void f1()
    {
        cout << "B::f1()" << endl;
    }
    virtual void f2()
    {
        cout << "B::f2()" << endl;
    }
public:
    int _b;
};

class C : public A
{
public:
    virtual void f1()
    {
        cout << "C::f1()" << endl;
    }
    virtual void f2()
    {
        cout << "C::f2()" << endl;
    }
public:
    int _c;
};

class D : public B, public C
{
public:
    virtual void f1()
    {
        cout << "D::f1()" << endl;
    }
    virtual void f3()
    {
        cout << "D::f3()" << endl;
    }
    void f4()
    {
        cout << "D::f4()" << endl;
    }
public:
    int _d;
};

int main()
{
    D d;
    d.B::_a = 1;
    d.C::_a = 2;
    d._b = 3;
    d._c = 4;
    d._d = 5;

    return 0;
}

描述

描述

菱形继承的虚继承对象模型

#include <iostream>
#include <stdio.h>

using namespace std;

class A
{
public:
    virtual void f1()
    {
        cout << "A::f1()" << endl;
    }
public:
    int _a;
};

class B : virtual public A
{
public:
    virtual void f1()
    {
        cout << "B::f1()" << endl;
    }
    virtual void f2()
    {
        cout << "B::f2()" << endl;
    }
public:
    int _b;
};

class C : virtual public A
{
public:
    virtual void f1()
    {
        cout << "C::f1()" << endl;
    }
    virtual void f2()
    {
        cout << "C::f2()" << endl;
    }
public:
    int _c;
};

class D : public B, public C
{
public:
    virtual void f1()
    {
        cout << "D::f1()" << endl;
    }
    virtual void f3()
    {
        cout << "D::f3()" << endl;
    }
    void f4()
    {
        cout << "D::f4()" << endl;
    }
public:
    int _d;
};

int main()
{
    D d;
    d.B::_a = 1;
    d.C::_a = 2;
    d._b = 3;
    d._c = 4;
    d._d = 5;

    return 0;
}

描述

描述

猜你喜欢

转载自blog.csdn.net/adorable_/article/details/80220064