There are two classes A & B, B is derived from A. A & B has virtual destructor. I also have two placement new functions:
void* operator new (std::size_t size, const char* file, int line)
void* operator new[](std::size_t size, const char* file, int line)
and two placement delete functions:
void operator delete (void* p, const char* file, int line)
void operator delete[](void* p, const char* file, int line)
The question is, if I have vector< A*> which contains either object A or object B, how do i destroy all elements in the vector properly ?
The problem is there is no delete expression for placement delete. I can't call the default delete expression because i need the file and line argument to be supplied so i am stuck with calling directly the operator delete, which i just found out that doing so won't call the object's destructor. I also can't explicitly call the destructor because the object inside the vector could be either A or B.
EDIT :
I tried to do v[i]->~A(); but B's destructor is not called even though there is a B object inside the vector
I populated the vector with something like this :
v[0] = new(__FILE__, __LINE__) A;
A* temp = new(__FILE__, __LINE__) B;
v[1] = temp;
Thank you
I believe your question can be simplified. It reduces to solving the following problem: Given
A * p = new ("foo", 1) A(/* args... */);
A * q = new ("bar", 2) B(/* args... */);
how do you release all the adherent ownership responsibilites?
First off, you have to destroy the objects:
p->~A();
q->~A();
Thanks to the virtual destructors, that's no problem.
Now you have to release the storage. How you do that depends on what exactly your placement-new allocation function does! For example, if you have:
void * operator new(std::size_t n, const char *, int)
{
return std::malloc(n);
}
Then you have to free memory by using std::free(p); std::free(q);. If your implementation instead used return ::operator new(n), then you would free with ::operator delete(p) etc. And if you're returning a pointer to static memory, then you don't have to free anything.
Since you're the one calling the placement-new allocation function, it's for you to know how to clean up!
Related
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).
Why the function with this signature
void* operator new (std::size_t size);
Cant be called in code like this
void* mem = new(100);
But rather it must be called like this
void mem = ::operator new(100);
Keyword new and operator new are different things.
Keyword new does:
Call operator new to allocate the memory. It can be overloaded for the type being allocated. Keyword new accepts optional arguments that get passed to operator new, this allows for placement new and non-throwing new syntax, see #include <new>.
Invoke the constructor of the object.
If the constructor throws, invoke the corresponding operator delete to free the memory. Note that if that operator delete is not accessible (not public), then new fails at compile time because operator delete cannot be called if the constructor throws and memory would be lost.
Keyword new cannot be overloaded, it always does these steps. This is operator new from step 1 what can be overloaded, normally along with operator delete from step 3.
In other words, X* p = new X(a, b, c); under the hood does something like (pseudo code):
X* p = static_cast<X*>(X::operator new(sizeof(X))); // 1. allocate memory
try {
p->X(a, b, c); // 2. invoke the constructor
}
catch(...) {
X::operator delete(p); // 3. free the memory if the constructor throw
throw;
}
In the above, if X does not overload its operator new, it is the global ::operator new that gets called. Note that X::operator new is implicitly static if overloaded.
There is a profound syntactical reason for this: placement new. With placement new, you can call a constructor without allocating memory for it. It's syntax is this:
#include <new>
Foo* foo = new(bufferPointer) Foo();
Obviously, part of this is the precise syntax you tried to call ::operator new()...
I have a point in my mind which I can't figure out about new operator overloading.
Suppose that, I have a class MyClass yet MyClass.h MyClass.cpp and main.cpp files are like;
//MyClass.h
class MyClass {
public:
//Some member functions
void* operator new (size_t size);
void operator delete (void* ptr);
//...
};
//MyClass.cpp
void* MyClass::operator new(size_t size) {
return malloc(size);
}
void MyClass::operator delete(void* ptr) {
free(ptr);
}
//main.cpp
//Include files
//...
int main() {
MyClass* cPtr = new MyClass();
delete cPtr
}
respectively. This program is running just fine. However, the thing I can't manage to understand is, how come new operator can be called without any parameter while in its definition it has a function parameter like "size_t size". Is there a point that I am missing here?
Thanks.
Don't confuse the "new expression" with the "operator new" allocation function. The former causes the latter. When you say T * p = new T;, then this calls the allocation function first to obtain memory and then constructs the object in that memory. The process is loosely equivalent to the following:
void * addr = T::operator new(sizeof(T)); // rough equivalent of what
T * p = ::new (addr) T; // "T * p = new T;" means.
(Plus an exception handler in the event that the constructor throws; the memory will be deallocated in that case.)
The new-expression new MyClass() is basically defined in two steps. First it calls the allocator function, which you have overloaded, to get some allocated memory. It passes the size of the type MyClass to that allocator function, which is why the size_t argument is required. After this, it constructs an object in that allocated memory and returns a pointer to it.
The compiler knows the size of your class. Basically, it's passing sizeof(MyClass) into your new function.
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.
I've been reading about overloading new and delete (and related topics like placement new/delete). One thing that is confusing me thus far is that operator delete's standard signature is (at class-scope):
void operator delete(void *rawMemory, std::size_t size) throw();
Delete is called like this:
MyClass* ptr = new MyClass;
delete ptr;
So, how does delete ptr; provide the second parameter for size? Also, can I assume that the MyClass* is implicitly converted to the void* in this circumstance?
Short Answer:
new and delete operators are overloaded at class scope for optimizing allocation for objects of specific class. But there can be special scenarios due to certain beasts like Inheritance which may lead to allocation requests for more than the class size itself,Since the very purpose of new and delete overload is special tuning for objects of size sizeof(Base), nothing larger or smaller, these overloaded operators should forward all other wrong sized memory requests to ::operator new and ::operator delete, to be able to do so, the size parameter needs to be passed as an parameter.
Long Answer:
Consider a Special Scenario:
class Base
{
public:
static void * operator new(std::size_t size) throw(std::bad_alloc);
};
class Derived: public Base
{
//Derived doesn't declare operator new
};
int main()
{
// This calls Base::operator new!
Derived *p = new Derived;
return 0;
}
In the above sample, because of inheritance The derived class Derived inherits the new operator of the Base class. This makes calling operator new in a base class to allocate memory for an object of a derived class possible. The best way for our operator new to handle this situation is to divert such calls requesting the "wrong" amount of memory to the standard operator new, like this:
void * Base::operator new(std::size_t size) throw(std::bad_alloc)
{
if (size != sizeof(Base)) // if size is "wrong," i.e != sizeof Base class
{
return ::operator new(size); // let std::new handle this request
}
else
{
//Our implementation
}
}
While overloading the delete operator, One must also ensure that since class-specific operator new forwards requests of the "wrong" size to ::operator new, One MUST forward "wrongly sized" deletion requests to ::operator delete, Since the original operators are guaranteed to to handle these requests in a standard compliant manner.
So the custom delete operator will be something like this:
class Base
{
public:
//Same as before
static void * operator new(std::size_t size) throw(std::bad_alloc);
//delete declaration
static void operator delete(void *rawMemory, std::size_t size) throw();
void Base::operator delete(void *rawMemory, std::size_t size) throw()
{
if (rawMemory == 0)
{
return; // No-Op is null pointer
}
if (size != sizeof(Base))
{
// if size is "wrong,"
::operator delete(rawMemory); //delegate to std::delete
return;
}
//If we reach here means we have correct sized pointer for deallocation
//deallocate the memory pointed to by rawMemory;
return;
}
};
Further Reading:
The following C++-Faq entry talks about overloading new and delete in a standard compliant way and might be a good read for you:
How should i write iso c++ standard conformant custom new and delete operators?
So, how does delete ptr; provide the second parameter for size?
If pointer type is a class type with a virtual destructor, from dynamic information about object type. If it doesn't have a virtual destructor and pointee type matches pointer type - from compile time information about type size. Otherwise delete ptr is undefined behavior.
It's the compiler's job to remember the size to release when you call delete. For delete ptr;, it will pass sizeof(MyClass), for delete[] ptr; it has to remember the number of MyClass in the array, and pass the correct size. I have absolutely no idea how this oddball corner of the language came to be. I can't think of any other part of the language where the compiler must remember a run-time value for you.