Malloc and Constructors

Malloc and constructors

Er...use new? That's kind of the point. You can also call the constructor explicitly, but there's little reason to do it that way

Using new/delete normally:

A* a = new A();

delete a;

Calling the constructor/destructor explicitly ("placement new"):

A* a = (A*)malloc(sizeof(A));
new (a) A();

a->~A();
free(a);

Malloc in constructors

First of all, you do not need a Heap* member variable inside your Heap object, and you certainly should not be allocating memory for it in the Heap constructor - that's just asking for trouble. Nor should you be accessing your member variables as H->Elements, but rather simply as Elements.

The only thing you need to allocate is the Elements array.

With regards to handling allocation failures, constructors should indicate failures via an exception. There is even a standard exception type, std::bad_alloc that is usually used to indicate a failure to allocate memory.

For example:

#include <stdexcept>  // for std::bad_alloc
...
Heap::Heap (int maxElements)
{
Elements = ( ElementType* ) malloc ( ( maxElements+1 )*sizeof ( ElementType ) );
if (Elements == NULL)
throw std::bad_alloc("Failed to allocate memory");
...
}

Even better, use new rather than malloc to allocate the memory. new will automatically throw an exception of type std::bad_alloc if it fails to allocate memory.

Example:

Heap::Heap (int maxElements) 
{
Elements = new ElementType[maxElements + 1]; // throws std::bad_alloc on failure
...
}

Note: if you use new to allocate the object, you must use delete to free it rather than free. (Correction: In the example above, you are using the array form of new, new[], so you should call the array form of delete, delete[]).

Finally, you haven't shown how ElementType is declared, but if it's a type that has a non-default constructor/destructor (or if it's a template parameter which means it can potentially be such a type), you have to use new rather than malloc when allocating it because malloc will not call the constructor (and free will not call the destructor). In general, it's good practice to just always use new and delete in C++ rather than malloc and free.

Calling constructor of class member when using malloc

malloc is a C function, and has no knowledge of classes, constructors, etc.

If you are dead-set on using this, you are going to have to always call placement new on each object (note: this requires including the header <new>), and also manually calling the destructor before deallocating. You will also need to store the pointer that is returned from placement new in order to avoid undefined behavior (as this is technically the only pointer that points to the start of the object's lifetime).

As you have in your edit, it will look something like:

auto cls = static_cast<MyClass*>(std::malloc(sizeof(MyClass)));
cls = new(cls) MyClass(/* any arguments here ... */);
cls->value = "hello";

cls->~MyClass();
std::free(cls);

However, be aware that by using malloc you actually introduce other issues with respect to exception safety. If your placement-new call throws an exception, the memory allocated by std::malloc will leak -- which is bad. You also wouldn't want placement new to operate on nullptr if malloc returns null, and would need to convey this error -- perhaps through an exception. You will probably want to wrap this into a helper that will ensure these conditions can't happen:

#include <new>     // placement-new, std::bad_alloc
#include <utility> // std::forward
#include <cstdlib> // std::malloc, std::free

template <typename T, typename...Args>
T* make(Args&&...args)
{
auto* p = static_cast<T*>(std::malloc(sizeof(T)));

// Placement new can't operate on nullptr
if (p == nullptr) {
// alternatively, this could be 'return nullptr' if not using exceptions
throw std::bad_alloc{};
}

try {
p = new (p) T(std::forward<Args>(args)...);
} catch (...) {
std::free(p);
throw; // rethrow the exception here
}
return p;
};

Similarly, you will likely want to have a way to destroy objects while deallocating them, so that resources aren't leaked:

template <typename T>
void dispose(T* p)
{
p->~T();
std::free(p);
}

Using these utilities, the above example now becomes:

auto cls = make<MyClass>(/* any arguments here ... */);
cls->value = "hello";

dispose(cls);

If this seems like an awful lot of boilerplate, this is because this is exactly what new and delete already does for you. If you can, you will be much better off using these facilities when dealing with C++ types. This also allows you to continue using other RAII-wrappers like smart pointers easily without having to manually specify allocators (shared_ptr) or deleters (unique_ptr).

There really isn't a good reason IMO to prevent using new and delete in favor of malloc/free when working with C++ code. If you have a library that exposes a C-only interface but is implemented in C++ code -- it would actually be better for you to internalize the allocations and switch to new and delete rather than using malloc and free so you can save yourself the hassle.1

One other thing to be aware of is that if the types allocated in your system ever have a custom alignment via alignas that exceeds alignof(std::max_align_t), using std::malloc will be undefined behavior because it will be unable to satisfy the alignment requirements. This is another thing that new/delete would take care of for you.


1 If your "mixed C++/C" code is exposing a C interface at some point, it would be much cleaner for you to use new and delete rather than malloc, and to simply expose the creation/deletion functions on your API explicitly. For example:

// C header:

extern "C"
my_class* make_my_class(void);

extern "C"
void dispose_my_class(my_class* p);

// C++ implementation:

extern "C"
my_class* make_my_class(void)
{
return new my_class{ ... };
}

extern "C"
void dispose_my_class(my_class* p)
{
delete p;
}

This is generally the approach taken for C/C++ interop.

malloc and constructors in c++

malloc allocates raw memory. There's no point in trying to pass constructor arguments to it because it doesn't call any constructors.

If you have to work with raw memory, it is up to you to construct an object in previously allocated raw memory by using "placement new" syntax

...
void *raw_b = malloc(sizeof *b);
b = new(raw_b) B_Class(c1, c2, c3); // <- placement new
...

Numerically, the value of b will be the same as raw_b, i.e. it is possible to get by without an extra raw_b pointer. But I prefer to do it this way, with an intermediate void * pointer, to avoid ugly casts.

Be careful when destroying such objects, of course. Custom allocation requires custom deletion. You can't just delete your b in general case. A symmetrical custom deletion sequence would involve an explicit destructor call followed by raw memory deallocation

b->~B_Class(); // <- explicit destructor call
free(b);

P.S. You might consider going a different route: overloading operator new/operator delete in your class instead of spelling out custom allocation explicitly every time you need to create an object.

malloc within constructor safe?

malloc/new within a constructor is safe, provided you follow the rule of three. With malloc/new you now have a resource that you have to explicitly take care to release at the right times.

Therefore: you must define a copy constructor, an assignment operator, and a destructor that will free the memory. If you don't, the class can be misused and cause you a lot of problems.

If you want to avoid having to define these extra functions, use std::vector instead, which handles them for you.

Using new with C++ Constructors that use C malloc

  1. Yes you can, but you should have a strong reason. The new operator invokes the constructor which in many ways is just like a regular class method. You should also add the free() part to the destructor.

    But please note, that if you malloc() a c++ object the constructor of that object will not be called. I think there are very few reasons to call malloc() in a c++ program: you probably wish to realloc() later, which is one reason. But you can always use native c++ objects like stl containers.

  2. It's not unsafe to use the two ways of allocating memory in a c++ program, you just have to be careful not to malloc() something and then delete it or new something and then free() it. But with malloc() you ALWAYS have to be careful about many things like it returning NULL and free()ing the allocated memory so it's not an extra thing.

    Once again, the most dangerous thing you can do is malloc() a c++ object. Because as I already said, the constructor won't be called.

C++ constructor: which exception type should I throw when malloc fails to allocate memory

The most appropriate exception would be to throw std::bad_alloc; however it is strongly discouraged to use malloc unless you have a good reason to -- and so I would advise against throwing this explicitly.

If you absolutely need heap memory, you should be using new/delete -- which will automatically invoke constructors/destructors, start/end object lifetimes, and will throw a std::bad_alloc when out of memory for you. std::malloc will result in undefined behavior for any non-trivial types because it doesn't formally start an object's lifetime.

Better yet, c++11 has been out for over ten years, so use std::unique_ptr and, if you have c++14, use std::make_unique so you don't even need new directly. unique_ptr will prevent memory leaks by ensuring that delete is called on the allocated pointer if the pointer is not-null.

This would now look like:

#include <memory>

// Note: 'typedef struct' does nothing in C++ -- there is no need for it
struct Point {
float x;
float y;
};

class Foo {
private:
std::unique_ptr<Point> p;
public:
Foo ()
: p{std::make_unique<Point>()}
{

}
};

Aside from the above answer: You shouldn't really use heap memory unless you really have a need for objects with dynamic lifetime. In the code you provided, it looks like it may be more appropriate to just hold the Point by value anyway:

class Foo {
private:
Point p;
public:
Foo ()
: p{} // no allocation needed
{

}
};


Related Topics



Leave a reply



Submit