Search code examples
c++virtual-functions

Virtual Functions of different classes share same (invalid?) memory address


Consider the following snippet:

#include<cstdio>
#include<iostream>
using namespace std;
class Class1{
    public:
    virtual void print(){
        cout << "Class1"<<endl;
        printf("%p\n", &Class1::print);
    }
};
class Class2{
    public:
    virtual void print(){
        cout << "Class2"<<endl;
        printf("%p\n", &Class2::print);
    }
};
class Class3{
    public:
    virtual void print(){
        cout << "Class3"<<endl;
        printf("%p\n", &Class3::print);
    }
};
int main(){
    Class1 c1;
    Class2 c2;
    Class3 c3;
    c1.print();
    c2.print();
    c3.print();
}

I compiled the following code on g++(MinGW) 8.2.0, and ran the output on cmd and Powershell. (I am not sure if this matters at all.) I expected the three addresses printed to be different. However, the three addresses printed are the same, in my case '00000001', which seems weird too, because this does not sound like a valid address(not a multiple of 4)

  • This occurs only when the keyword "virtual" is used.
  • Whether or not Class2 inherits from Class1 does not make any difference.(same with 2-3, 1-3, etc.)

As far as I know, virtual functions are implemented with something called a 'vtable'. I know that for each class there is a secret pointer that points to the table. But even if that is the case, shouldn't the addresses for each function be distinct, or at least legal?

I did look into other questions, such as: this, in which the function fork() was at the center of the issue; this.. and after looking at all those questions asking about "the same address" I thought, maybe this has something to do with the OS? But after this I failed to find anything relevant.


TL;DR:

1. The above code prints three same addresses. Why? How is this possible?

2. The address printed looks invalid(00000001). Why is this?


Solution

  • Your code has undefined behavior. &Class3::print is a pointer to a member function, but %p expects a void *. If you cast the pointer to a void * then you get valid results.

    #include<cstdio>
    #include<iostream>
    using namespace std;
    class Class1{
        public:
        virtual void print(){
            cout << "Class1"<<endl;
            printf("%p\n", (void*)&Class1::print);
        }
    };
    class Class2{
        public:
        virtual void print(){
            cout << "Class2"<<endl;
            printf("%p\n", (void*)&Class2::print);
        }
    };
    class Class3{
        public:
        virtual void print(){
            cout << "Class3"<<endl;
            printf("%p\n", (void*)&Class3::print);
        }
    };
    int main(){
        Class1 c1;
        Class2 c2;
        Class3 c3;
        c1.print();
        c2.print();
        c3.print();
    }
    

    outputs

    Class1
    0x400b50
    Class2
    0x400be0
    Class3
    0x400c70