Reputation: 3
Someone knows why the Latter syntax for calling , Dog::operator new is calling the default constructor after doing the allocation, ending up calling 2 constructors ?
I want to know if I'm doing something wrong, and how can I do:
Dog *ptr = new("arg") Dog();
without calling 2 constructors. and without using any trick, like checking in the default constructor if the object is already constructed. Here is the code:
class Dog
{
public:
Dog() // default
{
std::cout << "default Dog constructor [" << this << "]" << std::endl;
}
Dog(int x) // int argument
{
std::cout << "dog constructor int " << x << "[" << this << "]" << std::endl;
}
Dog(const std::string& word) // std::string argument
{
std::cout << "dog constructor std::string: " << word << " ["<< this << "]" << std::endl;
}
Dog(std::string &&word) // rvalue string argument
{
std::cout << "dog constructor std::string&& rvalue: " << word << " [" << this << "]" << std::endl;
}
// custom operator new
static void *operator new(std::size_t size) noexcept // for default constructor
{
Dog *ptr = (Dog*)malloc(size); // allocate memory
if (ptr) // if allocate ok
{
::new(ptr) Dog(); // call default constructor on object in memory
return ptr; // returns
}
else
return nullptr;
}
template<class T>
static void * operator new(std::size_t size, T&& value) noexcept // for argument constructor
{
Dog *ptr = (Dog*) malloc(size); // allocate the memory
if (ptr)
{
::new (ptr) Dog(std::forward<T>(value)); // pass the argument exactly as was passed to operator new,
// using perfect forwarding
return ptr;
}
else
return nullptr;
}
~Dog() { std::cout << "destructor " << std::endl; }
};
int main(void)
{
Dog *d = (Dog*) Dog::operator new(sizeof(Dog), "Const Char * Argument"); // argument version
Dog *d2 = (Dog*)Dog::operator new(sizeof(Dog)); // default constructor argument
//1 this works as expected, do what you specified in the member operator new, everything goes normal.
Dog *d3 = new Dog(); // default constructor
Dog *d4 = new("Const Char * Argument") Dog(); // argument constructor
// this is shorter, goes into your member operator new, BUT when it returns to this scope,
// call the default constructor for *d3, and for *d4 too.
// so this ends up calling constructors twice for both objects.
}
So, I was mixing allocation with construction, there's no reason for doing that here, maybe there is some use for that in operator new[] to construct the array with a contructor other than the default contructor.
But the the best way to define these members operators are:
class Dog {
public:
// .......
// custom operator new
static void *operator new(std::size_t size) noexcept // for default constructor
{
void *memory = malloc(size); // allocate memory
if (memory) // if allocate ok
{
return memory; // returns
}
else
return nullptr;
}
static void *operator new[](std::size_t size) noexcept
{
void *memory = malloc(size); // allocate memory
if (memory) // if allocate ok
{
return memory; // returns
}
else
return nullptr;
}
static void operator delete(void *block) noexcept
{
free(block);
}
static void operator delete[](void *block) noexcept
{
free(block);
}
~Dog() { std::cout << "destructor " << std::endl; }
};
int main(void)
{
// now we can use new operator normaly without complications
Dog *d1 = new Dog[10]; // default constructor on all objects
Dog *d2 = new Dog("const char * argument"); // call std::string&& constructor
delete[] d1;
delete d2;
}
Upvotes: 0
Views: 552
Reputation:
Using (keyword) new: It invokes the allocating operator new and (!) calls the constructor.
Note: When providing an operator new you should provide an operator delete, too (in your case it would call free). Also, do not forget the array versions.
Upvotes: 3