[C + +] polymorphism: dynamic_cast

Posted by Jalz on Mon, 10 Jan 2022 11:21:02 +0100

1,dynamic_ The role of cast

Function: converts the address of the base class to the address of the derived class. If the types match, the conversion succeeds. Otherwise, the conversion fails and returns an empty address.

Conversion principle:

dynamic_cast<B *>(p);
  • If p is a pointer to an object of type B, the conversion is successful;
  • If p is not a pointer to an object of type B, the conversion fails and returns a null address.

Code example:

#include <iostream>
using namespace std;

class A {
public :
    virtual void say() {
        cout << "Class A" << endl;
    }
};

class B : public A {
public :
    virtual void say() {
        cout << "Class B" << endl;
    }
};


class C : public A {
public :
    virtual void say() {
        cout << "Class C" << endl;
    }
};

int main() {
    srand(time(0));
    A *p;
    switch (rand() % 2) {
        case 0: p = new B(); break;
        case 1: p = new C(); break;
    }
    //Judge what p is
    //Convert p to destination address B*
    cout << dynamic_cast<B *>(p) << endl;
    //Convert p to destination address C*
    cout << dynamic_cast<C *>(p) << endl;
    return 0;
}

result:

0x7faf0c4059c0
0x0

That is, the conversion of p to b * address is successful, and the conversion to C * address fails, because p points to a pointer to an object of type B.

2,dynamic_ Relationship between cast and vtable

/*************************************************************************
	> File Name: dynamic_cast.cpp
	> Author: Maureen 
	> Mail: Maureen@qq.com 
	> Created Time: I 1 / 10 17:43:07 2022
 ************************************************************************/

#include <iostream>
using namespace std;

class A {
public :
    //virtual void say() {
    //    cout << "Class A" << endl;
    //}
};

class B : public A {
public :
    //virtual void say() {
    //    cout << "Class B" << endl;
    //}
};


class C : public A {
public :
    //virtual void say() {
    //    cout << "Class C" << endl;
    //}
};

int main() {
    srand(time(0));
    A *p;
    switch (rand() % 2) {
        case 0: p = new B(); break;
        case 1: p = new C(); break;
    }
    //Judge what p is
    //Convert p to destination address B*
    cout << dynamic_cast<B *>(p) << endl;
    //Convert p to destination address C*
    cout << dynamic_cast<C *>(p) << endl;
    return 0;
}

Compilation error:

dynamic_cast.cpp:42:13: error: 'A' is not polymorphic
    cout << dynamic_cast<B *>(p) << endl;
            ^                 ~
dynamic_cast.cpp:44:13: error: 'A' is not polymorphic
    cout << dynamic_cast<C *>(p) << endl;
            ^                 ~
2 errors generated.

Error reason:
dynamic_cast is a behavior that can only be implemented at run time, because dynamic_cast is used to convert the pointer of a base class, and the object pointed to by the base class pointer will be generated only when new comes out at runtime, so dynamic_cast must be runtime behavior.

As like as two peas, the programmer can't judge which address is the class, but can judge the content stored in the address, but the content can not be ordinary member attribute, because the members of the two classes may be exactly the same. So we can not judge the class of the address simply by the common member attribute. But there is a special kind of content, which is the virtual function table.

dynamic_ The key to the survival of cast is the virtual function table. It must find the virtual function table, because class B has its own virtual function table, and class A also has its own virtual function table. You can judge which class it belongs to according to the different virtual function tables.

So the reason for the above compilation error: after deleting the virtual function, there will be no virtual function table in the class, dynamic_cast won't work properly.

Thinking: according to the analysis, dynamic_cast is to find the virtual function table and experiment to verify dynamic_ Strong correlation between cast function and vtable.

#include <iostream>
using namespace std;

class A {
public :
   A() {
       cout << "A constructor" << endl;
   }
   //virtual void say() {
   //    cout << "Class A" << endl;
   //}

   virtual ~A() {
       cout << "A destructor " << endl;
   }
};

class B : public A {
public :
   B() {
       cout << "B constructor" << endl;
   }
   //virtual void say() {
   //    cout << "Class B" << endl;
   //}
   virtual ~B() {
       cout << "B destructor " << endl;
   }
};


class C : public A {
public :
   C() {
       cout << "C constructor" << endl;
   }
   //virtual void say() {
   //    cout << "Class C" << endl;
   //}
   virtual ~C() {
       cout << "C destructor " << endl;
   }
};


void judge(A *p) {
   if (dynamic_cast<B *>(p)) {
       cout << p << " is class B" << endl;
   }
   if (dynamic_cast<C *>(p)) {
       cout << p << " is class C" << endl;
   }
   return ;
}

int main() {
   srand(time(0));
   A *p;
   switch (rand() % 2) {
       case 0: p = new B(); break;
       case 1: p = new C(); break;
   }
   judge(p);
   delete p;

   A *p1 = new B(), *p2 = new C();
   judge(p1), judge(p2);
   //Exchange the addresses to vtable saved in p1 and p2
   swap(((void **)p1)[0], ((void **)p2)[0]);
   judge(p1), judge(p2);
   return 0;
}

Operation results:

A constructor
B constructor
0x7fd639d059a0 is class B
B destructor
A destructor
A constructor
B constructor
A constructor
C constructor
0x7fd639d059a0 is class B
0x7fd639d059b0 is class C
0x7fd639d059a0 is class C
0x7fd639d059b0 is class B

It can be seen that after exchanging the addresses to vtable saved in the objects pointed to by p1 and p2, dynamic_ The cast result is different, so dynamic is verified_ Cast and vtable are strongly correlated.

Topics: C++