Fonction virtuelle C ++ et analyse approfondie de la table virtuelle

1. Comment les fonctions virtuelles sont-elles trouvées

Regardez d'abord l'héritage unique, compilez le code suivant:

#include<iostream>
#include<string.h>
using namespace std;

class Grandam
{
public:
    virtual void introduce_self()
    {
        cout << "I am grandam." << endl;
    }
};

class Mother:public Grandam
{
public:
    virtual void introduce_self()
    {
        cout << "I am mother." << endl;
    }
};

class Daughter :public Mother
{
public:
    void introduce_self()
    {
        cout << "I am daughter." << endl;
    }
};

int main()
{
    Grandam* ptr;
    Grandam g;
    Mother m;
    Mother m1;
    Daughter d;

    typedef void(*introduce_self)(); 

    ptr = &g;
    ptr->introduce_self();

    ptr = &m;
    ptr->introduce_self();

        ptr = &m1;

    ptr = &d;
    ptr->introduce_self();

    introduce_self* p = (introduce_self*)(*(int*)(&g));  
    (*p)();

    system("pause");
    return 0;
}

Le résultat est montré dans la figure ci-dessous:

Écrivez la description de l'image ici

Le quatrième résultat est que la fonction virtuelle de la classe de base Grandam est appelée.

Dans le code:

typedef void(*introduce_self)();   //对函数进行重命名

introduce_self* p = (introduce_self*)(*(int*)(&g));  //定义一个函数指针,并让它指向对象内存的首地址里存放的地址(即虚表指针或地址)

(*p)();   //对虚表调用该函数

因为虚表里存放的是虚函数的地址,所以对函数指针进行解引用,即就是调用该虚函数。

Ainsi nous savons:

只要存在虚函数,那么被基类指针或被基类对象引用的对象中调用该虚函数时,该被调用对象的首地址存放的是虚表指针_vfptr(地址),而虚表_vfptr中存放的是虚函数地址。

2. Si la classe de base et la classe dérivée partagent une table virtuelle

Nous pouvons également le déboguer en une seule étape en appuyant sur f10 pour voir les changements dans le contenu stocké dans la mémoire, comme indiqué sur la figure:

1. La mémoire lors de l'appel de la fonction introd_self () de l'objet Grandam de la classe de base g est représentée dans la figure suivante:

Écrivez la description de l'image ici

2. La mémoire lors de l'appel de la fonction introduire_self () de la classe dérivée de l'objet Mère m est représentée dans la figure ci-dessous:

Écrivez la description de l'image ici

3. La mémoire lors de l'appel de la fonction introduire_self () de la classe dérivée de l'objet Mère m est représentée dans la figure ci-dessous:

Écrivez la description de l'image ici

4. La mémoire lors de l'appel de la fonction introduire_self () de la classe dérivée Daughter object d est indiquée dans la figure suivante:

Écrivez la description de l'image ici

Résumé: D'après la comparaison des trois mémoires, on sait que les pointeurs de table virtuelle stockés dans différentes adresses d'objets sont différents et que les objets du même type partagent une table virtuelle.

3. L'ordre dans lequel la classe de base et la classe dérivée appellent la fonction virtuelle

1. Héritage unique, le code est le suivant:

#include<iostream>
#include<string.h>
using namespace std;

class Grandam
{
public:
    void introduce_self1()
    {
        cout << "1: I am grandam." << endl;
    }

    void introduce_self2()
    {
        cout << "2: I am grandam." << endl;
    }

    virtual void introduce_self3()
    {
        cout << "3: I am grandam." << endl;
    }

    virtual void introduce_self4()
    {
        cout << "4: I am grandam." << endl;
    }
};


class Daughter :public Grandam
{
public:

    void introduce_self2()
    {
        cout << "2: I am daughter." << endl;
    }

    virtual void introduce_self5()
    {
        cout << "5: I am daughter." << endl;
    }

    virtual void introduce_self1()
    {
        cout << "1: I am daughter." << endl;
    }

    virtual void introduce_self4()
    {
        cout << "4: I am daughter." << endl;
    }

};

int main()
{
    Grandam g;
    Daughter d;
    Daughter* ptr = &d;

    typedef void(*intro)();
    intro* pi = (intro*)(*((int*)(&d)));
    while (*pi)
    {
        (*pi)();
        (int *)pi++;
    }
    return 0;
}

** Le code suivant est un appel à la fonction virtuelle dans la table virtuelle. Les précautions sont les suivantes:

typedef void(*intro)();
    intro* pi = (intro*)(*((int*)(&d)));
    while (*pi)
    {
        (*pi)();
        (int *)pi++;
    }

Remarque : il s'agit d'un appel à la table virtuelle de l'objet de classe dérivé d. Il est exécuté sous VS 2012. Comme la dernière adresse de la table virtuelle est 00000000, elle peut être écrite comme cette boucle while. Différents compilateurs ont des méthodes d'écriture différentes .

Les résultats de l'opération sont les suivants:

Écrivez la description de l'image ici

En résumé, on peut voir que sous héritage unique :
selon le pointeur de table virtuelle stocké dans l'adresse objet: l'ordre de l'adresse de la fonction virtuelle stockée dans la table virtuelle, pour appeler la fonction virtuelle:

1) Commencez par appeler les fonctions virtuelles de la classe de base et appelez-les dans l'ordre déclaré dans la classe de base.

2) Une fois la fonction virtuelle de la classe de base appelée, appelez la fonction virtuelle de la classe dérivée et placez-la derrière la fonction de classe de base.

3) Lorsque la fonction virtuelle de la classe dérivée est appelée, elle est également appelée dans l'ordre de déclaration dans la classe dérivée.

2. L'héritage multiple est le suivant:

#include<iostream>
#include<string.h>
using namespace std;

class Grandam
{
public:
    void introduce_self1()
    {
        cout << "1: I am grandam." << endl;
    }

    void introduce_self2()
    {
        cout << "2: I am grandam." << endl;
    }

    virtual void introduce_self3()
    {
        cout << "3: I am grandam." << endl;
    }

    virtual void introduce_self4()
    {
        cout << "4: I am grandam." << endl;
    }
};

class Mother 
{
public:
    virtual void introduce_self1()
    {
        cout << "1: I am mother." << endl;
    }

    virtual void introduce_self2()
    {
        cout << "2: I am mother." << endl;
    }
};

class Daughter :public Grandam , public Mother
{
public:

    void introduce_self2()
    {
        cout << "2: I am daughter." << endl;
    }

    virtual void introduce_self5()
    {
        cout << "5: I am daughter." << endl;
    }

    void introduce_self1()
    {
        cout << "1: I am daughter." << endl;
    }

    virtual void introduce_self4()
    {
        cout << "4: I am daughter." << endl;
    }

};

int main()
{
    Grandam g;
    Mother m;
    Daughter d;
    Mother& m1 = d;
    Grandam& g1 = d;
    typedef void(*pfun)();
    pfun* pg1 = (pfun*)(*(int*)(&g1));
    while (*pg1)
    {
        (*pg1)();
        (int*)pg1++;
    }

    pfun* pm1 = (pfun*)(*(int*)(&m1));
    while (*pm1)
    {
        (*pm1)();
        (int*)pm1++;
    }
    return 0;
}

Le résultat est le suivant:

Écrivez la description de l'image ici

Résumé : Dans le cas de l' héritage multiple , la fonction virtuelle de la classe dérivée est appelée après l'appel de la fonction virtuelle de la première classe de base.

Je suppose que tu aimes

Origine blog.csdn.net/lxp_mujinhuakai/article/details/69787395
conseillé
Classement