Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the issue with malloc() and virtual functions? [duplicate]

Possible Duplicate:
C++: why is new needed?

Why cant I use malloc to allocate space for my objects when they are children of a class containing virtual functions? This is really frustrating. Is there a good reason?

The following program illustrates the problem. It segfaults on line 27, where I call aa->f()

#include <iostream>
#include <cstdlib>

class A 
{
public:
    virtual int f() {return 1;}
};

class B 
{
public:
    int f() {return 1;}
};

class Aa : public A {};

class Bb : public B {};

int main()
{
    Aa* aa = (Aa*)malloc(sizeof(Aa));
    Aa* aan = (Aa*)new Aa();
    Bb* bb = (Bb*)malloc(sizeof(Bb));
    std::cout << bb->f() << std::endl;
    std::cout << aan->f() << std::endl;
    std::cout << aa->f() << std::endl;
    return 0;
}

Version info: g++ (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5

like image 625
Troels Blum Avatar asked Mar 15 '11 12:03

Troels Blum


4 Answers

A common way to implement virtual functions is to have a pointer to a "virtual table" or vtable at a negative offset from the object. This table is needed to figure out what virtual function to call. This is why just malloc'ing space doesn't work.

like image 178
DavidK Avatar answered Nov 18 '22 01:11

DavidK


malloc only allocates memory, but does not create an object. So, the line

Aa* aa = (Aa*)malloc(sizeof(Aa));

allocates a region of memory that is large enough to hold an A, but contains garbage. As others pointed out, this also means that the pointer to the vtable will not be set (I got that one from @David Rodríguez's comment on another answer), which is required to dispatch calls to virtual functions. Since B does not contain virtual functions, no such problem arises. It would happen with B too, however, if B contained any data-members that require initialization, such as this:

class B 
{
public:
    B() : foo(new int()) {}

    int f() {return *foo;}
private:
    int * foo;
};

The line

Aa* aan = (Aa*)new Aa();

can do without the cast:

Aa* aan = new Aa();
like image 34
Björn Pollex Avatar answered Nov 18 '22 01:11

Björn Pollex


The reason is that malloc knows nothing about C++ constuctors and consequently does not call them. You can call the constuctors yourself using placement new:

int main()
{
    Aa* aa = (Aa*)malloc(sizeof(Aa));
    new(aa)Aa;

    Aa* aan = (Aa*)new Aa();

    Bb* bb = (Bb*)malloc(sizeof(Bb));
    new(bb)Bb;

    std::cout << bb->f() << std::endl;
    std::cout << aan->f() << std::endl;
    std::cout << aa->f() << std::endl;

    aa->~Aa();
    free(aa);

    delete aan;

    bb->~Bb();
    free(bb);

    return 0;
}

Note that you have to manually call the destructors before freeing such memory.

like image 23
NPE Avatar answered Nov 18 '22 00:11

NPE


Don't use malloc, use new - malloc does not call constructors.

When you do A * a = new A(); the compiler will allocate memory, set up the vtable pointer for A and call the constructor. When you call a virtual function, the vtable is used to actually find the function.

When you do A * a = (A *) malloc(...); the compiler will allocate memory, which will contain random data. When you call a virtual function, it'll look at the (garbage) vtable and call some random location.

A class with virtual functions look something like this internally:

struct Foo {
  void * vtable;
  int aClassMemberVar;
};

Calling a virtual function looks at the "hidden" vtable pointer, which points to the class vtable, a linked list of pointers to functions. So this vtable pointer must be initialized, and malloc doesn't do that.

like image 35
Erik Avatar answered Nov 18 '22 02:11

Erik