Are There Any Issues with Allocating Memory Within Constructor Initialization Lists

Are there any issues with allocating memory within constructor initialization lists?

It's not exception-safe. If the new for j throws an exception then the destructor for Test is not called, and so the memory for i is not freed.

The destructor of i is called if the initializer for j throws, it's just that a raw pointer has no destructor. So you could make it exception-safe by replacing i with a suitable smart pointer. In this case, unique_ptr<int> for i and unique_ptr<int[]> for j would do.

You can rely on the initializers to be executed in their correct order (the order the members are defined, not necessarily the order in the list). They can safely use data members that have already been initialized, so there's no problem with using count in the initializer for k.

What is the right way to allocate memory in the C++ constructor?

I think the simplest way to do this would be to use a boost scoped array and let someone else's well tested library code handle it all for you.

So:

class Boda {
boost::scoped_array<int> memory;
public:
Boda(int length) : memory(new int [length]) {}
~Boda() {}
};

Moreover, scoped arrays cannot be copied - so you avoid the nasty copy constructor deallocation issue mentioned in another answer.

Using new in a member initializer list of constructor

Use:

Testclass():sources(new int[32]){}

This is using member-initialization-list which is the preferred way to initialize members.

By "safe" or "okay" you probably meant, whether it is exception-safe? What if new throws the bad_alloc exception?

Well, in that case, the destructor will not be called, because the object is not fully-constructed, as constructor-body is not executed. There may be a resource leak if you've acquired any resource in the initialization list.

Consider this,

class X
{
int *ints; // Order of declaration matters!
Huge *huges; // It ensures that huges will be initialized after ints

X() : ints(new int[32]), huges(new Huge[10000]) {}
};

If new Huge[10000] throws an exception, the memory allocated toints will leak!

In such cases, function-try-block can be useful. See these:

  • Function try blocks, but not in constructors
  • What is the purpose of a function try block?

If you think about this problem of exception-safety, you will soon realize that if a class manages just one resource, then the life will be easier. If a single class manages more than one resource, then you wouldn't be able to decide which one threw an exception in the member-initialization-list, and consequently, you wouldn't be able to decide which one is to be deallocated in the catch block of function-try-block. A resource leak is destined.

However, if a class needs more than one resource, then first you encapsulate each of the different type of resource in a class, and declare objects of these resource-managing class as a member of your class.

How come std::initializer_list is allowed to not specify size AND be stack allocated at the same time?

The thing is, std::initializer_list does not hold the objects inside itself. When you instantiate it, compiler injects some additional code to create a temporary array on the stack and stores pointers to that array inside the initializer_list. For what its worth, an initializer_list is nothing but a struct with two pointers (or a pointer and a size):

template <class T>
class initializer_list {
private:
T* begin_;
T* end_;
public:
size_t size() const { return end_ - begin_; }
T const* begin() const { return begin_; }
T const* end() const { return end_; }

// ...
};

When you do:

foo({2, 3, 4, 5, 6});

Conceptually, here is what is happening:

int __tmp_arr[5] {2, 3, 4, 5, 6};
foo(std::initializer_list{arr, arr + 5});

One minor difference being, the life-time of the array does not exceed that of the initializer_list.

Efficient ways to initialize class members; both heap and stack allocated

Why can't you do this?

SomeClass::SomeClass( void ) : 
mFoo(new Foo)
, mBar(new Bar)
{
}

They are raw pointers and no unnecessary copies are created.

I should also point out that the reason you use initializer lists is so that the object is in a valid state (that is, all members have valid values) when the constructor body is executed.

SomeClass::SomeClass( void )
{
//before this point, mFoo and mBar's values are unpredictable
mFoo = new Foo;
mBar = new Bar;
}

Regarding exceptions, the destructor of SomeClass will not be called ONLY if the exception is thrown inside the constructor itself.

Finally, regarding being thread safe or not, it depends on whether each thread has its own copy of SomeClass or not and whether SomeClass contains static members that are being written to.

Uncaught exception at constructor after allocating memory

Assuming you change the code so it compiles, the destructor of p2a (now p2b) will be called because it was successfully default-constructed. However, it will still hold NULL, because your attempt to reset it in the body of C::C fails.

The memory allocated by new B will be cleaned up automatically by the stack unwinding process. However, pint and psomeclass will both be leaked, because you're not using RAII for these members.

To clarify, let's step through the code:

C::C() {
objsomeclass = someclass();

psomeclass = new someclass();
pint = new int();

p2b.reset(new B);
/* in your code as posted, the call sequence is:
new B (allocate sizeof B)
-> B::B
-> new A (allocate sizeof A)
-> A::A which throws
<- new A failed, so memory is freed
<- B::B failed (initialized subobjects would be
destroyed here, but there are none)
new B failed, so memory is freed
*/
}

Note that:

  • all members are already default-initialized (because you didn't use the initializer list), so they all get destroyed when the body of C::C unwinds
  • if psomeclass and pint were smart pointers, this would release their dynamically-allocated memory. They aren't, so this is leaked.

In general, it is better style to use the initializer list and RAII.

For reference, maybe start with this (very old) article: GOTW 66

Initialization of values before constructor

Only thing that works is adding one structure that is holding informations. These informations are used later by constructor. This struct is defined in code file (.cpp) so it is invisible for other objects in program.

// Here we will save our values
struct {
Memory::BaseAllocator* allocator;
Memory::SystemInt size;
} MemoryObjectValues;

// we will take values from struct save them in attributes
Objects::MemoryObject::MemoryObject() {
this->_objectAllocator = MemoryObjectValues.allocator;
this->_objectSize = MemoryObjectValues.size;

MemoryObjectValues.allocator = nullptr;
MemoryObjectValues.size = 0;
}

// during allocation we will save values into struct
void* Objects::MemoryObject::operator new(size_t size, Memory::BaseAllocator* allocator) {
Objects::MemoryObject* newObject = static_cast<Objects::MemoryObject*>(allocator->allocateItem(size));
// set important values like size and pointer to allocator
MemoryObjectValues.allocator = allocator;
MemoryObjectValues.size = size;

return newObject;
}

What do I do if constructor fails to allocate memory in C++?

You should use new, not malloc. new throws std::bad_alloc when you are out of memory. An exception should be propagated from the constructor if you fail to allocate (or for any other reason have a problem with initialization), as this is the only way you prevent the destructor from being called. If the constructor successfully completes, the destructor must be called (unless, of course, it was heap allocated and never freed).



Related Topics



Leave a reply



Submit