I am learning the C++ placement new by the following code.
class Cell {
public:
Cell() {
printf("default constructor by %s\n", __func__);
}
Cell(int ina) : a(ina) {
printf("customized constructor.\n");
}
~Cell() {}
void* operator new(size_t); // Operator new.
void* operator new(size_t, Cell*p) {
return p;
}
private:
int a; // tmp variable.
};
// Global variable.
Cell global_cell;
void* Cell::operator new(size_t size) {
printf("start running the placement new\n");
Cell* ptr = new (&global_cell) Cell;
printf("the cell pointer is %p and the global address is %p\n", ptr, &global_cell);
return ptr;
}
int main() {
printf("====\n");
Cell *ptr = new Cell;
printf("====\n");
}
Here is the output I got:
default constructor by Cell
=====
start running the placement new
default constructor by Cell
the cell pointer is 0x60107c and the global address is 0x60107c
default constructor by Cell
=====
I know the first "default constructor" comes from the initiation of global_cell. But why I got two "default constructor" after that? Am I missing something about the placement new? Also, how can I implement the placement new with the second non-default constructor which takes an input integer?
new (&global_cell) Cell
in your operator new overload is one default construction, new Cell in mainis the other.
operator new is only supposed to allocate memory, not construct an object; the relevant constructor is called automatically afterwards.
Using placement new with your non-default constructor isn't very complicated:
new (&global_cell) Cell(2)
(That is, there's no difference to "regular" new.)
You're implementing the custom operator new for the class in the wrong way.
operator new for a class should just provide the raw memory where to put the instance, not create an object. Placement new instead initializes an object (using the default constructor in your case because you specified no parameters).
If you want to use placement new and passing parameter just write
new (memory_address) MyObject(arg1, arg2);
but note that using placement new is sort of an alternative to defining a custom operator new for the class. In the latter you just call normal allocation with
new MyObject(arg1, arg2);
to use your custom allocator (that however should just provide enough and properly-aligned memory).
You get them when you call "new Cell":
Cell *ptr = new Cell;
Cell* ptr = new (&global_cell) Cell;
After operator new is called, constructor is always called
Related
I've seen a few places that show how to overload the global new function for allocations. What I would like to do is to have any call to 'new' call my function, which simply wraps the C++ standard 'operator new' and tracks memory usage. But I have a problem of not knowing how to construct the object because the type of the object is not available in the function:
void* operator new (std::size_t size)
{
// I don't know what to construct
}
struct MyClass{ int members[8];}
new MyClass; // I'd basically like to wrap the standard 'new' function allocator.
You are not supposed to construct an object inside of an overloaded operator new, only allocate raw memory of the requested size and return a pointer to it. The object’s constructor will be called on the returned memory after the operator new exits.
A new expression calls operator new specifying the size of the type, then calls the type’s constructor on that memory, and then returns a pointer to the object that was created.
Thus, an expression like new MyClass in your example is roughly equivalent (not exactly) to this:
//MyClass *cls = new MyClass;
void *mem = :: operator new (sizeof(MyClass));
MyClass *cls = static_cast<MyClass*>(mem);
cls->MyClass();
And the converse is true for the delete expression, which calls the type’s destructor, and then calls operator delete to deallocate the raw memory, eg:
//delete cls;
cls->~MyClass();
:: operator delete (cls);
It seems you're looking for a Factory function. A factory is a dedicated function (or even a class) that produced instances of a class.
E.g.
#include <memory>
struct MyClass { int members[8]; };
std::unique_ptr<MyClass> MyClassFactory() {
auto inst = std::make_unique<MyClass>();
// assign some stuff to members
return inst;
}
In a situation where I want to avoid dynamic memory allocations, I'm replacing the new operator with a process that essentially uses the memory of some statically allocated object (the Storage class below). You can see a minimum working example below:
#include <cassert>
#include <iostream>
struct Object {
Object() { std::cout << "Creating a new object\n"; }
static void *operator new(size_t);
static void operator delete(void *p);
};
static struct {
Object where;
bool allocated = false;
} Storage; // 1
void *Object::operator new(size_t) {
assert(!Storage.allocated);
auto p = ::new (&Storage.where) Object; // 2
Storage.allocated = true;
return p;
}
void Object::operator delete(void *p) {
assert(Storage.allocated);
static_cast<Object *>(p)->~Object();
Storage.allocated = false;
}
int main() { Object *obj = new Object; } // 3
My question has to do with the number of calls to the constructor. When I run the above program, I expect to call the constructor twice (marked as 1 and 2 in the comments above) but the output I get is:
Creating a new object
Creating a new object
Creating a new object
Why is the constructor called thrice? I'd only expect constructor calls, by the static object and the call to placement new. I tried tracing the code with gdb, but it makes no sense to me, since position //3 is where the third call to the constructor originates.
The reason I want to know is because a case has emerged, where this extra constructor call causes unwanted side-effects; up until now, this extra call was unnoticed.
For some odd reason, your operator new calls the constructor when it should just allocate memory. This means that the call to new winds up calling the constructor of Object twice. There is one call in operator new and another call in main.
You probably want this:
void *Object::operator new(size_t) {
assert(!Storage.allocated);
Storage.allocated = true;
return reinterpret_cast<void *> (&Storage.where);
}
Imagine if the constructor took an integer parameter and the line in main looked like this:
Object *obj = new Object(7);
How would operator new know how to properly construct the object? That's not where you're supposed to do that!
Object *obj = new Object; does two things:
Allocates memory by calling operator new
Calls the constructor.
Your operator new calls the constructor as well, so the constructor is called twice by this statement (and once for the global variable initialization).
Note that delete is the same. delete obj; does two things:
Calls the destructor.
Deallocates memory by calling operator delete
Your operator delete shouldn't call the destructor either, because then the destructor is called twice.
I am trying to understand how the operator new can be overloaded and got this from the research I did online.
The new operator overload function takes in a size_t type. However in my main I am calling it using new Dummy(). From my understanding Dummy gets implicitly converted to size_t correct ? then what happens to () after Dummy ? shouldn't that result in an error ?
void* operator new(size_t sz)
{
void* m = malloc(sz);
std::cout<<"User Defined :: Operator new"<<std::endl;
return m;
}
class Dummy
{
public:
Dummy()
{
std::cout<<"Dummy :: Constructor"<<std::endl;
}
~Dummy()
{
std::cout<<"Dummy :: Destructor"<<std::endl;
}
};
int main()
{
Dummy * dummyPtr = new Dummy();
}
There are 2 things involving new keyword:
First you have the new operator, there are different overloads defined by the standard and you can override them or create your new ones. It's responsibility is allocating memory of N size and returning a void* to it. You can even create your own new operator that allocates memory and gets an extra parameter like this:
void* operator new(size_t sz, char x) // x is a placement param
{
std::cout << "User Defined :: Operator new" << std::endl;
return ::operator new(sz); // standard new
}
auto* x = new('*') int(234); // our overload
But wait a second, how does the new operator get the size of the type we want to instantiate??
Our friend the new expression just entered the game!
Think about the new expression like some syntactic sugar for calling the new operator. It has this structure:
new(placement_params) type+initializer
it means that for new int(22) the expression will be new(no placements) int + copy_initializer(22) and it will internally call the new operator passing the size of the type.
New operator will return a void* that points to the memory that it just allocated and the right-size of the new expression will initialize that memory. In this example calling the copy initializer
new is a language construct and not a regular function to it has special syntax and conveniences regarding its usage. There's differences between a new expression and operator new
The following both construct a Dummy but one uses local storage and the other uses dynamic storage. So in both cases Dummy() is part of the constructor call and, in the second case, not tied directly to new.
auto d1 = Dummy();
auto d2 = new Dummy();
Here the new expression both allocates space and constructs the Dummy in that space using the supplied constructor and arguments. The amount of space is determined by the compiler via sizeof Dummy.
When you overload the default operator new, it will use your user-defined function to do the allocation, but not the construction (that is done by the new expression).
We have a base class A, class B derived from A.
Now class B is allocated using placement new operator.
As a part of my code, I have added class C inside class A
Now the placement new just uses malloc of sizeof class B and does not take into account class C.
Class C constructor is called inside the Class B constructor. ( this is my suspect )
I want to know if thats correct. Or do we add the size of class C in the placement new allocation?
if we add up the memory, how do we call the constructor for class C, it would allocate the memory again?
card.h {
class Card
{
private:
char hwModel; // HW/FW model and rev are stored in the
// database to detect change of card type
char hwRev;
char serialNum[EM_CARD_SERIAL_NUM_SIZE];
char fwModel;
char fwRev;
>>>>
public:
class IssuResource *ptrIssuResrc;
void *operator new(size_t size, void *objP) {size = size; return objP;};
// overload the new operator
void operator delete(void *objP) { objP = objP;};
// overload the delete operator
Card();
}
chopperCard.cpp
// Allocate Memory and Create Card Object
/* sa_ignore */
buf = (UINT32 *)malloc(sizeof(CardChop));
if (buf == NULL) {
emLogError(EM_INTERNAL_ERROR, __FUNCTION__,
"exit: failed to allocate %d bytes for card object\n",
sizeof(CardChop)
);
exit(1);
}
/* sa_ignore */
card = new (buf) CardChop(spa_issu_restart); --> placement new
}
Constructor for choppercard:
{
CardChop::CardChop (boolean is_issu_set) : Card()
{
issu_inprogress = is_issu_set;
if (is_issu_set) {
ISSUTRACE(4,"Issu state is SET\n");
dev_np37xx_update_issu_state(is_issu_set);
dev_set_issu_state(is_issu_set);
dev_dm_issu_state(is_issu_set);
dev_tigris_issu_state(is_issu_set);
dev_evros_issu_state(is_issu_set);
dev_evros1_issu_state(is_issu_set);
>>>
vtwin->fvt->set_gp_stat(vtwin, VTWIN_GP_STAT_APP_TRACE1,
CHOP_BG_INIT_FAIL);
// init Bourget device
initDevice((dev_object_t *)bourget);
vtwin->fvt->set_gp_stat(vtwin, VTWIN_GP_STAT_APP_TRACE1, CHOP_BG_INIT_AFTER);
ptrIssuResrc = new IssuResource();
}
Placement new will work JUST like operator new without placement, except that it uses the supplied address rather than allocating from the heap. Since neither forms of operator new will in itself call the actual constructor, the construction is exactly the same either way - the compiler calls the object constructor something like this
T *p = new T;
becomes (not valid syntax, and T::operator new may become ::operator new):
T *p = T::operator new(sizeof(T)); p->T();
with placement, it's nearly the same:
T *p = new(somewhere) T;
becomes (not valid, as above):
T *p = T::operator new(sizeof(T), somewhere); p->T();
If you have something like this:
class C { ... };
class A
{
...
C c;
};
class B: public A
{
...
}
then C's constructor will be called automatically when the constructor of B is called. (And B's constructor will automatically call A's constructor)
The value returned from sizeof includes the sizes of all the bases and members. There is no other concept of size to confuse things.
It looks like the code is excessively complicated. What you are doing with malloc is simply reinventing the default behavior of new.
This does the same thing, but simpler:
card = new CardChop(spa_issu_restart);
if (card == NULL) {
emLogError(EM_INTERNAL_ERROR, __FUNCTION__,
"exit: failed to allocate %d bytes for card object\n",
sizeof(CardChop)
);
exit(1);
}
If using the global, default operator new, you should also remove the operator delete overload.
In fact, the best practice with placement new is not to use delete at all. (Note that you have defined operator delete to do nothing.) The better way is to use a pseudo-destructor call, which looks like
card_ptr->~CardChop(); // execute destructor but no delete
free( card_ptr ); // release placement memory a la delete
Also, note that exit is a potentially unsafe way to terminate the program as it doesn't destroy local objects. Consider throwing an exception instead.
void someCode()
{
char memory[sizeof(Foo)];
void* place = memory;
Foo* f = new(place) Foo();
}
and the signature of new placement operator
void * operator new(size_t, void *p)
{
return p;
}
I don't get how a constructor could be bind to void *p since a constructor doesn't return any values ?
EDIT:
the following code for instance doesn't compile :
struct A{};
void f(void * p)
{
}
int main()
{
f(A());
std::cin.ignore();
}
The problem here is that C++ new is confusing:
there is the operator new, which you can overload and is in charge of allocating raw memory
there is the new expression (such as new A(5)) which under the covers first call the corresponding operator new overload and then performs the construction
Note that you have no say as to how the construction is performed: you supplied the type and the constructor parameters, the language does the rest, and there is no way to change this behavior.
Therefore, when you overload/override operator new, you are only affecting the raw memory allocation. The full syntax of the new expression can clear things up:
new (somepointer) A(5)
^~~~~~~~~~~~~ ^~~~
The first part is the list of arguments passed to operator new, on top of the size of the object (always passed). The second part corresponds to the constructor call. If there is no parameter to be passed to new (apart from the size which you don't control) then the first part may be omitted yielding the typical form new A(5).
You are free to overload new as much as you want, for example:
new (alignof(A)) A(5);
will call an operator new overload accepting a size_t as supplementary argument (representing the necessary alignment for the type):
void* operator new(size_t size, size_t alignment);
note: the first argument (size) is always there.
So, placement new is just a fancy overload in its own right:
void* operator new(size_t, void* p) { return p; }
There is not much interest in such an operator (it does nothing), however it helps maintaining a consistent syntax and allows you a way to specify where to build an object via using the new expression so that it is not necessary to create another syntax to allow such operation.
Constructor called after operator new executed. You posted not equivalent code. Equvalent would be
struct A
{
constructor (void* this);
}
int main ()
{
A a = A::constructor(operator new ());
}
It is not c++, just pceudocode
I think that the source of your confusion is an assumption that new(place) Foo(); is a single operation; it is not. Here is what's going on:
A placement new operator is called to allocate memory
A constructor is called to initialize the allocated memory
The first call needs to return the value (a void*). It is a block of memory suitable to store your object. The second call is a constructor. It gets the value from the placement new operator as its hidden this parameter, and performs its regular initialization tasks on it.