What's the issue with malloc() and virtual fun

2020-03-24 04:30发布

问题:

This question already has answers here:
Closed 9 years ago.

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

回答1:

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.



回答2:

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();


回答3:

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.



回答4:

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.



回答5:

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



回答6:

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.



回答7:

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.