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

7

8

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

Renault answered 15/3, 2011 at 12:15 Comment(4)
Solution here: https://mcmap.net/q/1322593/-in-c-why-is-new-needed-to-dynamically-create-an-object-rather-just-allocation/…Aviation
@Tomalak: you should vote to close as duplicate if you can (I believe that with 5k rep you are allowed to)Palatial
The answer is simple. new is the correct way to create new instances of classes in C++. malloc is not.Grill
@David: It's not a duplicate. That the answers are the same does not mean that the questions are the same.Aviation
C
8

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.

Cordellcorder answered 15/3, 2011 at 12:17 Comment(2)
Ummm, yes. That's what I said, isn't it? I explicitly said a pointer to a virtual table, not the virtual table itself...Cordellcorder
I misread your answer also. I think the English is ambiguous. It could be read (as you intended) "... a pointer to a 'virtual table', which pointer is at a negative offset" or it could be misread "a pointer to a vtable, which vtable is at a negative offset". Still worth a +1.Favour
S
6

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();
Surmount answered 15/3, 2011 at 12:18 Comment(0)
M
6

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.

Maisey answered 15/3, 2011 at 12:19 Comment(1)
+1 Someone gave me a +1 for my small response of Placement New... I hadn't seen your more complete, so I copy my +1 to you :-) I'll note that your code is TRULY unreadable :-) :-) Some blank lines here an there could help.Denumerable
I
2

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.

Inae answered 15/3, 2011 at 12:17 Comment(5)
or create the vtable, which is the relevant item here.Aviation
I think it is important to differentiate the virtual method table (unique per type) from the vtable pointer (per object). In all implementations that I know that use vtable s, the table exists regardless of whether any object of the type is instantiated. The particular problem here is that the vtable pointer in this particular object is uninitialized, and when trying to call any virtual method, the memory located at the random address will be interpreted as a vtable.Palatial
@Tomalak: new doesn't create the vtable.Inae
I didn't say that it does. Malloc doesn't create it either.Aviation
@Tomalak: neither malloc nor new nor the constructor will create the virtual method table (in all implementations I have seen). The vtable is usually created at compile time, then adjusted at link time and when the OS loads the binary into memory, but it exists prior to any instantiation of any object of the type.Palatial
V
1

Surely because the Virtual Function Table isn't getting created properly?

Vinegarette answered 15/3, 2011 at 12:18 Comment(2)
The pointer to the table will not be set, but the table will exist in the system.Palatial
@David: that's a slightly better way of saying the same thing! :)Vinegarette
C
0

malloc does not call the constructor of the class, so your object is not initailized properly, hence it seg faults. Use new to allocate memory when using C++. BTW, there is no need to cast the pointer returned from new.

Cubitiere answered 15/3, 2011 at 12:18 Comment(0)
J
0

The good reason is called virtual tables. Objects of types that have virtual methods have a table of pointers pointing to the address of the actual virtual methods to be called. These are called virtual tables or v-tables.

Jarlath answered 15/3, 2011 at 12:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.