开发者

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

开发者 https://www.devze.com 2023-02-16 18:42 出处:网络
This question already has answers here: Closed 11 years ago. Possible Duplicate: C++: why is new needed?
This question already has answers here: Closed 11 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()

#incl开发者_StackOverflowude <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


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.


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


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.


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.


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


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消