This question already has answers here:
Difference between 'new operator' and 'operator new'?
(8 answers)
Closed 8 years ago.
Interview question: what's the difference between "new" operator and "new" function?
I answered there is no difference, that they run the same code, but interviewer kept needling me like that was the wrong answer.
Is it the wrong answer? Or was the interviewer just playing games with me?
If it's the wrong answer, what's the right answer?
I continued that the "new" operator could be overloaded if you needed a custom allocation, but then he wanted to know how to overload it. Of course I didn't have that answer, having never had the need, but I told him I could look it up in 10 minutes (which is never the right answer in an interview).
So anyhow, having done some research on "new" operator vs. "new" function and not seeing any really satisfying answers, I thought I'd ask the specific question.
The new operator and operator new are not the same thing.
The new operator calls an operator new function to allocate memory, and then, depending on the type allocated and the syntax used, initializes or calls a constructor on the allocated memory. In other words, operator new forms only a part of the operation of the new operator.
operator new is the function called to allocate memory by the new operator. There's a default implementation of operator new which can be replaced, which is not the same thing as overloading. operator new can also be implemented for a particular type to handle allocations only of objects of that type, or operator new can be overloaded and the overload can be selected by using the placement new form of the new operator.
The default implementations of operator new can be replaced by defining functions with the following signatures:
void *operator new(std::size_t size);
void *operator new(std::size_t size, const std::nothrow_t&);
void *operator new[](std::size_t size);
void *operator new[](std::size_t size, const std::nothrow_t&);
When you provide a replacement or overload for operator new you should provide corresponding operator delete functions:
void operator delete(void* ptr) noexcept;
void operator delete(void* ptr, const std::nothrow_t&) noexcept;
void operator delete[](void* ptr) noexcept;
void operator delete[](void* ptr, const std::nothrow_t&) noexcept;
To provide an overload of operator new for use with the placement form of the new operator you can add additional arguments (the nothrow versions of operator new and operator delete do this).
struct my_type {};
void *operator new(std::size_t size, const my_type&);
void operator delete(void *ptr, const my_type&);
new (my_type()) int(10); // allocate an int using the operator new that takes a my_type object
There is no 'placement delete' form of the delete operator. The overload of operator delete is provided because if an error occurs during the initialization/construction of the memory (e.g., the constructor called by the new operator after operator new has been called) the corresponding operator delete is called if it exists before re-throwing the exception. Otherwise operator delete is not called and the memory leaks when the exception is thrown.
Basically:-
Function: "operator new"
class Example
{ public:
void* operator new( size_t );
}
"new operator":
Example* eg = new Example();
I continued that the "new" operator could be overloaded if you needed
a custom allocation
You are almost right.
new is a keyword for an operator, which is used for memory allocation.
Why is this an operator ?
On need basis, it can be overloaded as a function both at global scope or class scope (but NOT namespace scope!). This wouldn't have been possible if it was a function.
The difference is in how they function. The initial allocation part is, per standard I believe, the same. That is, using syntax new vs operator new() explicitly is very much the same. The difference, is using new initializes or constructs the new object. There is also 3 different versions of ::operator new() and there is various syntaxes to utilize them as well (i.e., placement new).
There is no new function. My guess is that they wanted you to say that one of these allocated the memory (the standard uses allocator function, function new operator and new operator for it) and that the other used the first to allocate memory, then called the constructor (the standard uses new expression) and the deallocator function (aka function delete operator or delete operator) to free the memory if the constructor exited with an exception.
Related
Basically, my question is whether the following code is valid.
void* mem = operator new(sizeof(T));
T* instance = new(mem) T;
delete instance;
If it isn’t, I’d be curious to know whether there is a way to split allocation and initialization for an object that is going to be deleted through delete obj;.
This does appear to be valid, as long as T is not an array type, and is not a class with a custom operator delete visible from its scope which is incompatible with the operator new you invoke.
[expr.delete]/2 says the operand of delete may be "a pointer to a non-array object created by a previous new-expression". The new-expression grammar syntax symbol does include the placement new syntax, and [expr.delete] doesn't say the previous new-expression needs to be a non-placement variety. And any new-expression including a placement new is considered to "create" an object.
The operator new allocation function and operator delete deallocation function involved do need to match up. operator new(sizeof(T)) will normally call the global ordinary allocation function, but to be more sure you can write ::operator new(sizeof(T)). The deallocation function is looked up in the scope of class T, so a static operator delete(void*); in a class could break this.
You might want to consider exception safety, though. A straight T* p = ::new T; is actually more equivalent to:
void* mem = ::operator new(sizeof(T));
T* p;
try {
p = ::new(mem) T;
} catch (...) {
::operator delete(mem, sizeof(T));
throw;
}
Your code is given almost exactly in the same form, as an explicit example, in the C++ standard:
21.6.2.3 Non-allocating forms [new.delete.placement]
void* operator new(std::size_t size, void* ptr) noexcept;
Returns: ptr.
Remarks: Intentionally performs no other action.
[ Example: This can be useful for constructing an object at a known
address:
void* place = operator new(sizeof(Something));
Something* p = new (place) Something();
— end example ]
Add a delete, after that, and you get what you wrote in your question.
I have a project that tightly controls what and how can allocate memory. I have a base class for things that may be allocated on a heap, with static overloads for operator new and operator delete and their array variants. These work perfectly without any warnings at all.
There's an ultimate base class for everything that allows placement new only:
class Object
{
public:
static void* operator new(size_t, void*);
static void* operator new[](size_t, void*);
static void operator delete(void*, void*);
static void operator delete[](void*, void*);
};
The implementations are trivial and in the corresponding .cpp file. operator news return the pointer, operator deletes don't do anything.
When I compile this under VS2015, using new (ptr) DerivedFromObject() generates the following warning. Exception handling is set to /EHa.
warning C4291: 'void *Object::operator new(std::size_t,void *)': no matching operator delete found; memory will not be freed if initialization throws an exception
I've tried messing around with the signatures: adding noexcept, adding size_t to operator delete but nothing seems to work. What's the correct form of static member placement operator delete?
It seems that I need to declare operator deletes in every derived class to get rid of the warning.
I have an object of type MyType that, for SSE reasons, needs to be 16-byte aligned. So, I wrote an allocator and overloaded the new operators. Methods in MyType:
inline static void* operator new(size_t size) {
awesome::my_allocator<MyType,16> alloc;
return alloc.allocate(size);
}
inline static void* operator new[](size_t size) { return operator new(size); }
inline static void operator delete(void* ptr) {
awesome::my_allocator<MyType,16> alloc;
alloc.deallocate(reinterpret_cast<MyType*>(ptr),0); //last arg ignored in my impl
}
inline static void operator delete[](void* ptr) { operator delete(ptr); }
Now, for cache-locality reasons, I need to copy-construct an instance into a particular piece of 64-byte-aligned memory:
void MyType::copy_into(uint8_t* ptr) const {
new (reinterpret_cast<MyType*>(ptr)) MyType(*this);
}
GCC tells me:
error: no matching function for call to ‘MyType::operator new(sizetype, MyType*)’
ICC tells me:
error : function "MyType::operator new" cannot be called with the given argument list
1> argument types are: (unsigned __int64, MyType *)
As I understand it, the placement new operator is provided by the C++ implementation (or possibly by <new>, which I also tried #includeing?) and simply returns its argument (new makes memory available, and placement new is the programmer saying that the given memory is available).
Curiously, the error does not occur when the (ordinary!) new operators defined above are not in the class. Indeed, MyOtherType, which doesn't define them, works just fine.
Question: what's going on? How should I fix it?
Since you have defined operator new in your class, you need to use the global new to use the placement version of it.
#include <new>
...
::new (reinterpret_cast<MyType*>(ptr)) MyType(*this);
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.
I know that you can use a dummy "int" parameter on operator++ and operator-- to override the postfix versions of those operators, but I vaguely recall something about a dummy parameter that you could declare on a destructor. Does anyone know anything about that, and if so, what that dummy parameter did?
This was in my old Turbo C++ tutorial books, which I read when I was a teenager (i.e. a long time ago), so I might be completely misremembering it. That was also very early C++, before it was standardized, so it's possible that it was something Turbo C++-specific.
You're possibly thinking of the placement and nothrow forms of operator delete, which have the signatures:
void operator delete(void *, void *) throw();
void operator delete(void *, const std::nothrow_t&) throw();
void operator delete[](void *, void *) throw();
void operator delete[](void *, const std::nothrow_t&) throw();
These are never called during normal operation, but would be used in the case where the constructor for an object being constructed with placement new throws an exception. Generally you don't have to define them, since the compiler already called the destructor(s) on the dead object's bases and members, and for placement new there's no memory to be freed. But can exist if you are overloading placement new and need a corresponding operator.
The second argument is not really used, and just distinguishes the signature for the ordinary:
void operator delete(void *)
These aren't special dummy arguments the way the operator++ ones are, though. They're just an instance of the general rule that call to new with extra arguments, such as:
obj = new(x,y,z) Object(a,b,c)
will generate implicit code to clean up from constructor errors that passes those same additional arguments to the operator delete, which will function (approximately) like:
void *raw = operator new(sizeof(Object), x,y,z)
try {
obj = new(raw) Object(a,b,c);
} catch(...) {
operator delete(raw,x,y,z);
throw;
}
Either you are misremembering, or you should try to forget it. Destructors don't have parameters, return types and they shouldn't throw exceptions.
I swear I've heard the same thing, but the C++ FAQ seems to say that there is no such form.
Perhaps you are thinking of placement new?
class MyClass { /* ... */ };
char * raw_mem = new char [sizeof (MyClass)];
pMyClass = new (raw_mem) MyClass;
// ...
pMyClass-->(~MyClass());
delete[] raw_mem;
You're not crazy. I have definitely seen an int parameter in a destructor before. Using HP's compiler on OpenVMS, I compiled a sample program show below. The list of symbols does include an destructor with an int parameter. I can only guess this is compiler specific.
$ create foo.cxx
class foo
{
~foo() {}
};
$ cxx foo.cxx
$ type [.CXX_REPOSITORY]cxx$demangler_db.
CX3$_ZN3FOOD1EV31GNTHJ foo::$complete$~foo()
CX3$_ZN3FOOD2EV30KQI3A foo::$subobject$~foo()
CX3$_ZN3FOOD9EV36HH9SB foo::~foo(int)
CXXL$_ZDLPV void operator delete(void *)