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 *)
Related
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.
Consider this code
// Example program
#include <iostream>
#include <string>
class SmartPtr{
int *ptr;
public:
explicit SmartPtr(int *ptr=0){
ptr = new int;
}
~SmartPtr(){
std::cout<<"Dest is called"<<std::endl;}
int& operator *() //overloaded operator for dereferencing
{
std::cout<<"(*) is called .."<<std::endl;
return *ptr;
}
int * operator ->() //overloaded operator for referencing
{
std::cout<<"(*) is called .."<<std::endl;
return ptr;
}
};
int main()
{
SmartPtr p(new int());
*p = 20; //overloaded operator * called
std::cout<<*p<<std::endl; // Overloaded operator * called
// End of program
//Destructor called
}
here i have overloaded operators * and & , i want to understand why that type of function signature based on the comments i have mentioned in main function .
If someone can explain how it works ?
PS : I am new to C++ and explaination i am asking is trivial {i know :)}
This is the way smart pointers are constructed.
They are wrappers of pointers of any class and they control the deallocation of these objects by automatically calling their destructor when the smart pointer goes out of scope.
In order to expose the interface of the wrapped class, they must overload * and -> operators and return the wrapped object's pointer.
In C++ you can define functions as behavior of operators on your types. That is exactly, what happens here.
int& operator * () declares the function, that is used for the unary * operator. The result of that operator then is a reference to an int.
int* operator -> ()then declares the function for ->. This one is a bit special, since it will be used on the return value again, until there is a pointer, which will be used to apply the standard behavior of ->.
After you have declared these operators, the compiler will accept them on any object of your type. You can then write *myObj go get the result of the operator * () function.
This feature gives you the power to extend the language in parts. Note, that the return types can be chosen as needed. If you look at advanced libraries, that implement domain specific languages, you'll find a lot of that. Boost::spirit is an extreme example of that.
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);
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.
I'm dang certain that this code ought to be illegal, as it clearly won't work, but it seems to be allowed by the C++0x FCD.
class X { /* ... */};
void* raw = malloc(sizeof (X));
X* p = new (raw) X(); // according to the standard, the RHS is a placement-new expression
::operator delete(p); // definitely wrong, per litb's answer
delete p; // legal? I hope not
Maybe one of you language lawyers can explain how the standard forbids this.
There's also an array form:
class X { /* ... */};
void* raw = malloc(sizeof (X));
X* p = new (raw) X[1]; // according to the standard, the RHS is a placement-new expression
::operator delete[](p); // definitely wrong, per litb's answer
delete [] p; // legal? I hope not
This is the closest question I was able to find.
EDIT: I'm just not buying the argument that the standard's language restricting arguments to function void ::operator delete(void*) apply in any meaningful way to the operand of delete in a delete-expression. At best, the connection between the two is extremely tenuous, and a number of expressions are allowed as operands to delete which are not valid to pass to void ::operator delete(void*). For example:
struct A
{
virtual ~A() {}
};
struct B1 : virtual A {};
struct B2 : virtual A {};
struct B3 : virtual A {};
struct D : virtual B1, virtual B2, virtual B3 {};
struct E : virtual B3, virtual D {};
int main( void )
{
B3* p = new E();
void* raw = malloc(sizeof (D));
B3* p2 = new (raw) D();
::operator delete(p); // definitely UB
delete p; // definitely legal
::operator delete(p2); // definitely UB
delete p2; // ???
return 0;
}
I hope this shows that whether a pointer may be passed to void operator delete(void*) has no bearing on whether that same pointer may be used as the operand of delete.
The Standard rules at [basic.stc.dynamic.deallocation]p3
Otherwise, the value supplied to operator delete(void*) in the standard library shall be one of the values returned by a previous invocation of either operator new(size_t) or operator new(size_t, const std::nothrow_t&) in the standard library, and the value supplied to operator delete[](void*) in the standard library shall be one of the values returned by a previous invocation of either operator new[](size_t) or operator new[](size_t, const std::nothrow_t&) in the standard library.
Your delete call will call the libraries' operator delete(void*), unless you have overwritten it. Since you haven't said anything about that, I will assume you haven't.
The "shall" above really should be something like "behavior is undefined if not" so it's not mistaken as being a diagnosable rule, which it isn't by [lib.res.on.arguments]p1. This was corrected by n3225 so it can't be mistaken anymore.
The compiler doesn't really care that p comes from a placement new call, so it won't prevent you from issuing delete on the object. In that way, your example can be considered "legal".
That won't work though, since "placement delete" operators cannot be called explicitly. They're only called implicitly if the constructor throws, so the destructor can run.
I suppose you might get away with it (on a particular compiler) if
new/delete are implemented in terms of malloc/free and
the placement new actually uses the same mechanism for keeping track of the destructor associated with allocations as the standard new, so that the call to delete could find the right destructor.
But there is no way it can be portable or safe.
I found this in the library section of the standard, which is about as counter-intuitive a location (IMO) as possible:
C++0x FCD (and n3225 draft) section 18.6.1.3, [new.delete.placement]:
These functions are reserved, a C ++
program may not define functions that
displace the versions in the Standard
C ++ library (17.6.3). The provisions
of (3.7.4) do not apply to these
reserved placement forms of operator
new and operator delete.
void* operator new(std::size_t size, void* ptr) throw();
void* operator new[](std::size_t size, void* ptr) throw();
void operator delete(void* ptr, void*) throw();
void operator delete[](void* ptr, void*) throw();
Still, the section defining legal expressions to pass to delete is 5.3.5, not 3.7.4.