The first point to keep in mind is the disclaimer: none of this is guaranteed by the standard. The standard says what the code should look like and how it should work, but it doesnโt really indicate exactly how it should happen by the compiler.
However, in fact, all C ++ compilers work in this respect in exactly the same way.
So, let's start with non-virtual functions. They come in two classes: static and non-static.
The easiest way is to use static member functions. A static member function is almost like a global function that is a friend class, except that it also needs a class name as a prefix to the function name.
Non-static member functions are a bit more complicated. They are still normal functions that are called directly, but they are passed by a hidden pointer to the instance of the object on which they were called. Inside the function, you can use the this to refer to the data of this instance. So, when you call something like a.func(b); , the generated code is very similar to the code you get for func(a, b);
Now consider virtual functions. Here, where we get the pointers in vtables and vtable. We have enough indirect attention that it is probably best to draw some diagrams to see how all this is laid out. Here's a pretty simple example: one instance of one class with two virtual functions:

Thus, the object contains its data and a pointer to the vtable. Vtable contains a pointer to each virtual function defined by this class. However, it may not be immediately apparent why we need to touch this. To understand this, let's look at the following (all so slightly) more complicated case: two instances of this class:

Note that each instance of the class has its own data, but both of them have the same vtable file and the same code. If we had more instances, they would still share one vtable among all instances of the same class.
Now consider derivation / inheritance. As an example, let's rename our existing class to "Base" and add a derived class. Because I feel creative, I will call him Derived. As above, the base class defines two virtual functions. A derived class overrides one (but not the other) of them:

Of course, we can combine these two, having several instances of each of the base and / or derived classes:

Now let's delve into this a bit more. Interestingly, we can pass a pointer / link to an object of a derived class to a function written to get a pointer / link to a base class, and it still works, but if you call a virtual function, you get a version for the actual class, not the base class . So how does it work? How can we treat an instance of a derived class as if it were an instance of a base class, and it still works? To do this, each derived object has a "subobject of the base class." For example, consider the following code:
struct simple_base { int a; }; struct simple_derived : public simple_base { int b; };
In this case, when you instantiate simple_derived , you get an object containing two int s: a and b . a (part of the base class) is at the beginning of the object in memory, and b (the derived part of the class) follows this. Thus, if you pass the address of an object to a function waiting for an instance of the base class, it uses the parts (s) that exist in the base class, which the compiler places with the same offsets in the object, d be in the object of the base class, so the function can manipulate them without even knowing that she is dealing with an object of a derived class. Similarly, if you call a virtual function, all it needs to know is the location of the vtable pointer. How important this is, something like Base::func1 basically means that it follows the vtable pointer, and then uses a pointer to a function with some given offset from it (for example, a fourth function pointer).
At least now I will ignore multiple inheritance. This adds a rather complicated picture (especially when virtual inheritance is involved), and you did not mention it at all, so I doubt that you really care.
As for access to any of them or using in any way, besides just calling virtual functions, you can think of something for a specific compiler, but do not expect it to be portable at all. Although things like debuggers often need to look at things like this, the code used tends to be pretty fragile and compiler specific.