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
andpint
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
Print MACro Values Without Knowing the Amount of MACros
Is There a Reason Why Not to Use Link-Time Optimization (Lto)
C++ Object Instantiation VS Assignment
How to Access a Global Variable Within a Local Scope
What Is Void* and to What Variables/Objects It Can Point To
Load Shared Library by Path at Runtime
Win32 Setforegroundwindow Unreliable
Global Variable "Count" Ambiguous
PDF Specifications for Coders: Adobe or Iso
What Is the Purpose of C++20 Std::Common_Reference
Qapplication in Non-Main Thread
Why Don't I Need to Specify "Typename" Before a Dependent Type in C++20
How to Determine the Correct Size of a Qtablewidget
How to Place a MACro in a Namespace in C++
Copy a Std::Vector to a Repeated Field from Protobuf with Memcpy