Currently I am working on custom memory allocation and one of the drawbacks is that I have to write multiple lines to achieve the same result that the new-expression provides with just one simple call.
Simple initialization:
MyClass *obj = new MyClass(3.14);
Less simple initialization:
void *obj_mem = alloc->Allocate(sizeof MyClass, alignof(MyClass));
MyClass *obj = new(obj_mem) MyClass(3.14);
I am going to provide my project group with allocators such as that one, and want them to actually use them, instead of falling back on calling new
, since we'll need these faster allocators to manage our memory.
But for that to happen, I will have to devise the simplest possible syntax to initialize a variable with my custom allocators.
My best bet was overriding operator new
in each class, since it is the allocation function for the new-expression.
class MyClass
{
...
void* operator new(size_t size, Allocator *alloc)
{
return alloc->Allocate(size, alignof(MyClass));
}
}
And then the syntax to initialize a variable becomes what I ultimately want:
MyClass *obj = new(alloc) MyClass(3.14);
However, it would be great if I could have a general equivalent of the above. So I wouldn't have to override operator new
for each class.
Variable initialization in C++ There are two ways to initialize the variable. One is static initialization in which the variable is assigned a value in the program and another is dynamic initialization in which the variables is assigned a value at the run time.
Different ways of initializing a variable in Cint a, b; a = b = 10; int a, b = 10, c = 20; Method 5 (Dynamic Initialization : Value is being assigned to variable at run time.)
Initialization of a variable provides its initial value at the time of construction. The initial value may be provided in the initializer section of a declarator or a new expression. It also takes place during function calls: function parameters and the function return values are also initialized.
In C++, class variables are initialized in the same order as they appear in the class declaration. Consider the below code. The program prints correct value of x, but some garbage value for y, because y is initialized before x as it appears before in the class declaration.
Kill new
entirely. You have to bundle the creation with destruction anyhow.
template<class T>
struct destroy {
Alloc* pool = nullptr;
void operator()(T* t)const {
ASSERT(t);
t->~T();
ASSERT(alloc);
alloc->Dealloc( t );
}
};
template<class T>
using my_unique_ptr = std::unique_ptr<T, destroy<T>>;
namespace details{
template<class T, class...Args>
my_unique_ptr<T> my_make_unique( Alloc* alloc, Args&&...args ) {
void* p_data = alloc->Allocate(sizeof(T), alignof(T));
try {
T* ret = ::new(p_data) T(std::forward<Args>(args)...);
return {ret, destroy<T>{alloc}};
} catch (...) {
alloc->Dealloc( p_data );
throw;
}
}
}
/// usual one:
template<class T, class...Args>
my_unique_ptr<T> my_make_unique( Alloc* alloc, Args&&...args ) {
return details::my_make_unique<T>( alloc, std::forward<Args>(args)... );
}
// permit leading il:
template<class T, class U, class...Args>
my_unique_ptr<T> my_make_unique( Alloc* alloc, std::initializer_list<U> il, Args&&...args ) {
return details::my_make_unique<T>( alloc, il, std::forward<Args>(args)... );
}
// for {} based construction:
template<class T>struct tag_t{using type=T;};
template<class T>using no_deduction=typename tag_t<T>::type;
template<class T>
my_unique_ptr<T> my_make_unique( Alloc* alloc, no_deduction<T>&&t ) {
return details::my_make_unique<T>( alloc, std::move(t) );
}
now my_make_unique
takes an Alloc*
and construction arguments, and it returns a smart pointer with destruction code bundled.
This unique pointer can be passed to a std::shared_ptr<T>
implicitly (via move).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With