In C ++, why is the β€œnew” necessary for dynamically creating an object, and not just for highlighting?

I have this trivial class hierarchy:

class Base { public: virtual int x( ) const = 0; }; class Derived : public Base { int _x; public: Derived( int x ) : _x(x) { } int x( ) const { return _x; } }; 

If I use malloc to host the Derived instance and then try to access the polymorphic function x , the program crashes (I get a segmentation error):

 int main( ) { Derived *d; d = (Derived*) malloc( sizeof(Derived) ); *d = Derived( 123 ); std::cout << d->x() << std::endl; // crash return 0; } 

Of course, my actual application is much more complicated (this is a kind of memory pool).


I am sure that due to the way I allocate d : I did not use new .

I know the placement of the new operator, which should be what I need, but I never used it and got some questions:

  • Why does my application crash if I do not use new ?

    What does new really do?

    Why can't I just use the assignment operator to assign a value to Derived( 123 ); the area of ​​memory pointed to by d ?

  • Do I need to use new for non-polymorphic types?

    How about a POD?

  • In C ++ Faq, which I linked above , it says that the memory area passed to the new placement should be aligned for the object I'm creating.

    I know what alignment is, but I don't know how to check the alignment needed for my class.

    malloc manual says:

    The malloc () and calloc () functions return a pointer to the allocated memory, which is appropriately aligned for any variable.

    And I hope that the alignment needed for my class is the class size returned by sizeof , so any address in the form address_returned_by_malloc + i * sizeof(my_class) is suitable for highlighting my objects.

    I hope my hopes?

+6
c ++ new-operator malloc alignment memory
source share
5 answers

Go down the line

  • Why does my application crash if I do not use a new one?

The virtual table is corrupt.

The virtual table gets stuck right after the allocated memory. when you are a new class, the generated code will correctly configure vtable. However malloc will not initialize vtable correctly

To see the virtual table, run g ++ -fdump-class-hierarchy

 Vtable for Derived Derived::_ZTV7Derived: 3u entries 0 (int (*)(...))0 8 (int (*)(...))(& _ZTI7Derived) 16 Derived::x Class Derived size=16 align=8 base size=12 base align=8 Derived (0x10209fc40) 0 vptr=((& Derived::_ZTV7Derived) + 16u) <-- notice how this is part of the structure Base (0x10209fcb0) 0 nearly-empty primary-for Derived (0x10209fc40) 

For the same reason, without operator = overloading, the generated assembler code will only copy data, not vtable [again, the compiler only knows to copy data, not vtable]

If you want to see a pointer based version with a valid vtable function:

 Derived e(123); d = &e; 
  • Should new ones also be used for non-polymorphic types?

If you use virtual functions, then yes, even for non-polymorphic types

  • I hope that the alignment needed for my class is the class size returned by sizeof, so any address in the form address_returned_by_malloc + I * sizeof (my_class) is suitable for placing my objects.

Alignment is not a problem.

+3
source share

Because malloc does not call the constructor of the class and does not know anything about any specific alignment requirements that it may have. If you need to use malloc (not recommended), look at the location of the new (provided that you do not want to overload the regular new for any reason).

+3
source share

Classes with virtual members contain a pointer to the so-called vtable - basically a table of function pointers to implement these virtual members. When you use operator new , the constructor is called, which, even if it is an implicit constructor, will correctly set this pointer to vtable.

However, malloc does not call the constructor. The vtable pointer remains uninitialized, pointing to some random memory. When you then try to call a virtual function, you look for a bad pointer and a failure (undefined behavior).

The solution is to use the new location to initialize the object before using it:

 int main( ) { Derived *d; d = (Derived*) malloc( sizeof(Derived) ); new(d) Derived(123); // invoke constructor // You could also do: // new(d) Derived; // *d = Derived( 123 ); std::cout << d->x() << std::endl; // crash // Although in your case it does not matter, it good to clean up after yourself by // calling the destructor d->~Derived(); return 0; } 

Some important points:

  • Alignment is not a problem. The memory from malloc is correctly aligned for any type of C ++.
  • Appointment with = does not help. The default implementation = copies all member variables, but the vtable pointer is not a member and is not copied.
  • Design is not required for POD types. Non-POD types may or may not require it (this behavior is undefined if you do not). In particular, the constructor also calls member variable constructors; therefore, if you do not create an external object, internal objects can also be broken.
+2
source share

I do not believe that the constructor of an object is called when using malloc.

+1
source share

the [basic.life] section of the standard says

The lifetime of an object is a property of the runtime of an object. An object is said to have non-trivial initialization if it has a class or aggregate type, and it or one of its members is initialized with a constructor other than the default trivial constructor. [Note: initialization with the trivial copy / move constructor is non-trivial initialization. - end note] The lifetime of an object of type T begins when:

  • stores with proper alignment and size for type T and
  • If the object has non-trivial initialization, its initialization is complete.

Since your class has virtual members, it requires non-trivial initialization. You cannot assign an object whose life time has not begun, you must initialize it with new .

+1
source share

All Articles