How does C++ visit member function and virtual member function

Preconditions

Before watching this blog, I suggest you already known what is virtual function and already known some basic stuff about C++ class.

Question

Recently I am reviewing the memory layout of C++ class and I have encountered the following series of problems:

  1. When we query the size of a class, why size of member functions (not virtual) don’t occupy memory.
  2. When a class derive from a class that has virtual functions, how many virtual table point does it has.

Answer 1

For the first question, the answer is during the compilation stage, every call to a member function of a class is translated. We know that after a function is compiled, it will be placed into the read-only code segment, so the address of this function won’t change during the ruining time.

c - Why are instructions addresses on the top of the memory user space  contrary to the linux process memory layout? - Stack Overflow

In addition, for any member function, it has a hidden parameter corresponding to the instance that calling it:

lea    -0x20(%rbp),%rax # put instance into rax, then into rdi, which represent the first parameter
mov    %rax,%rdi
call   0x55555555524c <ChildA::test()>

Since we have such nice properties, the compiler simply translate each function call into above format, so we don’t need to store a pointer inside a class to point to its member function.

Answer 2

I thought the answer is it contains two vpointers, the memory layout of child class looks like:

----- child
| Vpointer
| SomethingElse
|----- Parent
|| Vpointer
|| SomethingElse
|-----
-----

The reason is I found that I can still access parent’s virtual function by doing child.Parent::function(), but it is wrong because this is in fact call though another mechanism rather than using vtable: Instead of finding the function in the vtable, it finds Parent::function() in the code segment, and pass the instance (derived instance, but it is still acceptable by base class’s function) into the function.

#include <iostream>
using namespace std;
class BaseA {
public:
    virtual void F1() {
        printf("something A");
    };
};

class ChildA : public BaseA{
public:
    void F1(){
        printf("something B");
    }
};

int main() {
    ChildA a;
    a.BaseA::F1();
    a.F1();
}

So the answer to question 2 is only 1 vpointer.