user5749114
user5749114

Reputation:

Change vtable memory after constructor allocation?

This was a question asked to me in an interview...

Is it possible to change the vtable memory locations after it's 
created via constructor? If yes, is it a good idea? And how to do that? 
If not, why not?

As i don't have that in depth idea about C++, my guess is that, it's not possible to change vtable after it's created!

Can anyone explain?

Upvotes: 0

Views: 147

Answers (2)

Ulrich Eckhardt
Ulrich Eckhardt

Reputation: 17424

The proper response to that question is simple: This question can't be answered. The question talks about the "vtable memory locations" and then continues with "after it's created via constructor". This sentence doesn't make sense because "locations" is plural while "it" can only refer to a singular.

Now, if you have a question concerning the typical C++ implementations that use a vtable pointer, please feel free to ask. I'd also consider reading The Design and Evolution of C++, which contains a bunch of background infos for people that want to understand how C++ works and why.

Upvotes: 1

SashaMN
SashaMN

Reputation: 708

The C++ standards do not tell us how dynamic dispatch must be implemented. But vtable is the most common way.

Generally, the first 8 bytes of the object is used to store the pointer to vtable, but only if the object has at least 1 virtual function (otherwise we can save this 8 bytes for something else). And it is not possible to change the records in the vtable during run-time.

But you have memset or memcpy like functions and can do whatever you want (change the vtable pointer).

Code sample:

#include <bits/stdc++.h>

class A {
    public:
    virtual void f() {
      std::cout << "A::f()" << std::endl;
    }
    virtual void g() {
      std::cout << "A::g()" << std::endl;
    }
};

class B {
    public:
    virtual void f() {
      std::cout << "B::f()" << std::endl;
    }
    virtual void g() {
      std::cout << "B::g()" << std::endl;
    }
};

int main() {
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(nullptr);
    A * p_a = new A();
    B * p_b = new B();
    p_a->f();
    p_a->g();
    p_b->f();
    p_b->g();
    size_t * vptr_a = reinterpret_cast<size_t *>(p_a);
    size_t * vptr_b = reinterpret_cast<size_t *>(p_b);
    std::swap(*vptr_a, *vptr_b);
    p_a->f();
    p_a->g();
    p_b->f();
    p_b->g();
    return 0;
}

Output:

A::f()
A::g()
B::f()
B::g()
B::f()
B::g()
A::f()
A::g()

https://ideone.com/CEkkmN

Of course all these manipulations is the way to shoot yourself in the foot.

Upvotes: 1

Related Questions