c++ Polymorphism from the Perspective of c

Keywords: Red Hat Programming

Catalog
1. Overview
2. Preparatory knowledge points before starting
  • 2.1 Do you know what different types of pointers mean?
  • 2.2c file is compiled into a target file. What does the target file contain?
3.c++ Static Polymorphism
  • 3.1 Actual coding operation
  • 3.2 Implementation Principle
  • 3.3 Understanding from the Perspective of c
4.c++ Dynamic Polymorphism
  • 4.1 Actual coding operation
  • 4.2 Implementation Principle
  • 4.3 Understanding from the Perspective of c
5. Compile and run environment
  • 5.1 Operating System
  • 5.2 Compiler

1. Overview

c + + is a hybrid programming language, which supports both object-oriented and process-oriented, and mainly object-oriented. Among the three characteristics of c + +: inheritance, encapsulation and polymorphism, polymorphism is the most difficult to understand. This paper will interpret the polymorphism of c + + from the perspective of c +.

2. Preparatory knowledge points before starting

2.1 Do you know what different types of pointers mean?

Different pointer types mean different ways of resolving the same memory start address. Here's a chestnut.

  • Code test4.c
#include <stdint.h>
#include <string.h>
#include <malloc.h>
#include <stdio.h>

int32_t g_int = 10;

int main()
{
    //Request 5 byte heap memory
    void * p = malloc(5);
    //The value of each byte is set to 90
    memset(p, 90, 5);        

    //Declare a char pointer pc to the starting address of the allocated memory
    char * pc = (char *)p;
    //Declare an int32_t pointer pint to the starting address of the allocated memory
    int32_t * pint = (int32_t *)p;

    //Take a byte of memory from the starting address to parse it into char variables
    if (*pc == 90) 
    {
        printf("char yes\n");
    }

    //Take four bytes of memory from the start address to resolve to the int32_t variable
    if (*pint == 90 + (90 << 8) + (90 << 16) + (90 << 24))
    {
        printf("int yes\n");
    }

    return 0;
}
  • Operation results

Different pointer types. png
  • Detailed illustration

Different pointer types. jpg

2.2c file is compiled into a target file. What does the target file contain?

  • At least include function symbols, global variables (if global variables are defined in the file).
  • From the gcc and nm commands, we can see that test4.o generated by the test code above contains a global variable g_int, a defined function symbol main, three undefined function symbols malloc, memset, put.
  • Command execution results

Object file. png

3. c++ Static Polymorphism

3.1 Actual coding operation

  • Code test5.cpp
#include <iostream>
using namespace std;

void fun1()
{
 cout << "fun1 call" << endl;
}

void fun1(int a)
{
 cout << "fun1 a call" << endl;
}

int main()
{
 fun1();
 fun1(10);
 return 0;
}
  • Operation results

Function re-signature.png

3.2 Principle of Implementation

  • Through the characteristics of overload, it is decided to call that function at the compilation stage, so it is called static polymorphism.
  • C + + compiler will re-sign function symbols when compiling code (c compiler will not). When C + + compiler encounters overload calls, it directly calls the re-signed function. When we use the nm command to view the symbols of executable files, we can see two re-signed symbols.

3.3 Understanding from the Perspective of c

#include <stdio.h>

void _Z4fun1v()
{
 printf("fun1 call\n");
}

void _Z4fun1i(int a)
{
 printf("fun1 a call\n");
}

int main()
{
 _Z4fun1v(); //Corresponding to void fun1();
 _Z4fun1i(10); //Corresponding to the previous void fun1(int a);
 return 0;
}

4.c++ Dynamic Polymorphism

4.1 Actual coding operation

  • Code test6.cpp
#include <iostream>
using namespace std;

class Base
{
public:
 virtual void sleep()
 {
 cout << "Base sleep" << endl;
 }
 virtual void eat()
 {
 cout << "Base eat" << endl;
 }
 virtual void run()
 {
 cout << "Base run" << endl;
 }
};

class Animal : public Base
{
public:
 size_t age;

 void sleep()
 {
 cout << "Animal sleep" << endl;
 }
 void eat()
 {
 cout << "Animal eat" << endl;
 }
 void run()
 {
 cout << "Animal run" << endl;
 }
};

/*
 Define a function pointer type, which is void () (Animal *);
 Used to point to virtual functions sleep, eat, run;
 The reason why there is an additional Animal* parameter here is because of the non-static member functions of the c++ class.
 By default, the compiler adds parameters to the class pointer at the beginning of the parameter list
 */
typedef void (* pFun)(Animal * animal);

int main()
{
 Animal dargon;
 Animal dog;

 Base * pBase = &dargon;
 Base & pRe = dog;

 //Dynamic polymorphism is achieved by pointing the base class pointer to the derived class object
 pBase->sleep();
 //Dynamic polymorphism is achieved by referencing a base class to a derived class object
 pRe.sleep();

 /*
 Remove the virtual table pointer for Animal.
 (size_t *)&dargon -> dargon Start address translated to size_t*
 *(size_t *)&dargon -> dargon The starting address starts with sizeof(size_t) bytes parsed into size_t (virtual table pointer value)
 (size_t *)*(size_t *)&dargon -> Convert this value to size_t*type

 ps: size_t The size of the pointer variable is the same as that of size_t, which is 4 bytes on the 32-bit computer and 8 bytes on the 64-bit computer.
  */
 size_t * vptable_dargon = (size_t *)*(size_t *)&dargon;
 size_t * vptable_dog = (size_t *)*(size_t *)&dog;

 cout << "size_t size = " << sizeof(size_t) << endl;

 //A class shares a virtual table pointer
 if (vptable_dog == vptable_dargon)
 {
 cout << "vptable value is equal" << endl;
 }

 //Traversal virtual table pointer
 while (*vptable_dargon)
 {
 //Take out each virtual table function
 pFun fun = (pFun)(*vptable_dargon);
 //Call each virtual table function
 fun(&dargon);
 vptable_dargon++;
 }

 return 0;
}
  • Operation results

c + + dynamic polymorphism. png

4.2 Implementation Principle

  • It is realized by the override feature of c++. Only at runtime can we know what function is actually called, so it is called dynamic polymorphism.
  • c + + adds a virtual function table (static variables of the class) to each class with virtual functions, and inserts a virtual table pointer to it at the starting address of each class object, through which runtime polymorphism can be achieved.

c++ Object Memory Layout. jpg

4.3 Understanding from the Perspective of c

  • Code test7.cpp
#include <stdio.h>
#include <malloc.h>
#include <string.h>

//Global static virtual table pointer, simulation class static virtual table pointer
static size_t * pBaseVptable = NULL;
static size_t * pAnimalVptable = NULL;

struct Base
{
    size_t * vptable;    //Analog virtual table pointer
};

struct Animal
{
    struct Base base;    //Simulated Animal Inheritance Base
    size_t age;
};

typedef void (* pFun)(Base * pBase);

void baseSleep(Base * pBase)
{
    printf("Base sleep\n");
}
void baseEat(Base * pBase)
{
    printf("Base eat\n");
}
void baseRun(Base * pBase)
{
    printf("Base run\n");
}

void animalSleep(Animal * pAnimal)
{
    printf("Animal age[%d] sleep\n", pAnimal->age);
}
void animalEat(Animal * pAnimal)
{
    printf("Animal age[%d] eat\n", pAnimal->age);
}
void animalRun(Animal * pAnimal)
{
    printf("Animal age[%d] run\n", pAnimal->age);
}

//Base structure initialization
void baseInit(Base * pBase)
{
    pBase->vptable = pBaseVptable;
}

//Initialization of Animal Structures
void AnimalInit(Animal * pAnimal)
{
    pAnimal->base.vptable = pAnimalVptable;
}

/*
    Virtual table pointer initialization
 */
void vptableInit()
{
    pBaseVptable = (size_t *)malloc(sizeof(size_t) * 4);
    pAnimalVptable = (size_t *)malloc(sizeof(size_t) * 4);

    memset(pBaseVptable, 0x0, sizeof(size_t) * 4);
    memset(pAnimalVptable, 0x0, sizeof(size_t) * 4);

    //Base class global virtual table initialization
    pBaseVptable[0] = (size_t)&baseSleep;
    pBaseVptable[1] = (size_t)&baseEat;
    pBaseVptable[2] = (size_t)&baseRun;

    //Animal class global virtual table initialization
    pAnimalVptable[0] = (size_t)&animalSleep;
    pAnimalVptable[1] = (size_t)&animalEat;
    pAnimalVptable[2] = (size_t)&animalRun;
}

void callVirtualFun(Base * pBase, int index)
{
    pFun fun = (pFun)pBase->vptable[index];
    fun(pBase);
}

int main()
{
    //Virtual table initialization
    vptableInit();
    Base * pBase = NULL;
    Animal * pAnimal = (Animal *)malloc(sizeof(Animal));

    //Simulated Object Initialization
    AnimalInit(pAnimal);
    pAnimal->age = 99;

    //Analog base class pointer to derived class
    pBase = (Base *)pAnimal;

    //Simulated call virtual function
    callVirtualFun(pBase, 0);
    callVirtualFun(pBase, 1);
    callVirtualFun(pBase, 2);

    return 0;
}
  • Operation results

c-simulated dynamic polymorphism.png

5. Compile and run environment

  • operating system

    [root@iZ940zytujjZ ~]# uname -m -s
    Linux x86_64

  • Compiler

    [root@iZ940zytujjZ ~]# gcc --version
    gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-4)
    [root@iZ940zytujjZ ~]# g++ --version
    g++ (GCC) 4.4.7 20120313 (Red Hat 4.4.7-4)

Posted by cshaw on Tue, 16 Jul 2019 12:52:35 -0700