Let's say we have the following code:
class MyType
{
public: int x;
}
MyType* object = new MyType();
delete object;
Is there a possible to check if object has specific value of x before deletion?
For example, if object.x is odd number, the object should not be free from memory after delete is called. In a few words to make a custom delete for this class where we can choose when the object can be free at operator delete call.
What is the real issue you are trying to solve?
To begin with, this Q&A is a good lesson to the common scenario in C++ of:
The language allowing you to do something, but just because you can doesn't mean you should.
These scenarios are commonly run into when trying to solve an XY problem.
In this particular case, instead of trying to overload operator delete (solving the Y problem, to much confusion of the clients of MyType) you are likely looking for something entirely different (the X problem), say a resource manager that take responsibility of MyType resources w.r.t. if and when they should be deleted (or not), based on e.g. object traits (such as oddness of a data member). See e.g. #Ayxan Haqverdili's answer for a minimal example.
A look at theoretically solving the misidentified Y problem
Don't do this, ever, for this kind of use case.
You can overload operator delete (as well as operator delete[]), by defining it as a static member function of the class for which you want to overload it (lookup precedence by lexical scope). As pointed out in the comments, whilst overloading the usual deallocation functions is legal, trying to conditionally allow for it to be called more than once is not:
struct MyType final {
int x;
static void operator delete(void *p) {
if (static_cast<MyType *>(p)->x % 2 == 1) {
::operator delete(p);
}
}
};
int main() {
MyType *object = new MyType{42};
delete object; // Not DEALLOCATED.
// Underlying object DESTRUCTED, however.
// Extremely confusing.
++(object->x);
delete object; // UB: destructor for underlying object called twice.
}
as per [expr.delete]/6 [emphasis mine]:
If the value of the operand of the delete-expression is not a null pointer value and the selected deallocation function (see below) is not a destroying operator delete, the delete-expression will invoke the destructor (if any) for the object or the elements of the array being deleted.
As a second attempt (emphasis: don't ever go down this road), we could avoid the destructor being called (only calling it conditionally) by overloading and abusing the (new as of C++20) class-specific destroying deallocation functions:
#include <iostream>
#include <new>
struct MyType final {
int x;
static void operator delete(MyType *p, std::destroying_delete_t) {
if (p->x % 2 == 1) {
p->~MyType();
::operator delete(p);
}
}
~MyType() {
std::cout << "\ndtor";
}
};
int main() {
MyType *object = new MyType{42};
delete object; // Neither deallocated nor underlying object destructed.
++(object->x);
delete object; // Destructed and deallocated.
}
Afaict this program is well-formed, but it does abuse the new destroying delete overloads. Clang does emit a -Wmismatched-new-delete warning, which is typically a hint for UB, but it may be inaccurate here.
Anyway, this seems like a good place to stop the fruitless journey of addressing this particular Y problem.
Yes:
if (object->x % 2 == 1)
delete object;
Note that raw pointers should be used rarely and with a lot of care.
Related
Suppose I'm implementing some class A that has a clear() method that should set the object state to the "brand new" state as if it has just been created with a constructor:
I should free all the resources that the current object is using (exactly the same thing A::~A() does),
and then I should initialize those resources again (exactly the same thing A::A() does).
So my initial idea was as follows:
void A::clear() {
this->~A();
*this = A();
}
However, I was told that this code causes UB since I cannot dereference this after calling its destructor. But at the same time I was told a better idea as well: if we use placement new, there is no dereferencing, so this actually might work:
void A::clear() {
this->~A();
new (this) A();
}
This feels extremely uncomfortable and as error-prone as it gets... So is this code actually valid?
is this code actually valid?
Your code is valid. It's also a rather unusual approach to merely change the object's value.
The assignment operator alone, if implemented correctly, will achieve this.
void A::clear() {
*this = A();
}
If this code does not work as expected, your assignment operator has been incorrectly implemented.
If you have a class without a destructor:
struct A {
~A() = delete;
};
The standard does not let me "locally" allocate an instance of that class:
int main()
{
A a; //error
}
But it seems like it is ok if I allocate that on free-store:
int main()
{
a *p = new A();
}
As long as I dont call delete on that pointer:
int main()
{
a *p = new A();
delete p; //error
}
So my question is, why does the standard let me have a class without a destructor if I allocate it on free-store? I would guess there are some use cases for that? But what exactly?
So my question is, why does the standard let me have a class without a destructor if I allocate it on free-store?
Because that's not how standard features work.
The = delete syntax you're talking about was invented to solve a number of problems. One of them was very specific: making types that were move-only or immobile, for which the compiler would issue a compile-time error if you attempted to call a copy (or move) constructor/assignment operator.
But the syntax has other purposes when applied generally. By =deleteing a function, you can prevent people from calling specific overloads of a function, mainly to stop certain kinds of problematic implicit conversions. If you don't call a function with a specific type, you get a compile-time error for calling a deleted overload. Therefore, =delete is allowed to be applied to any function.
And the destructor of a class qualifies as "any function".
The designed intent of the feature was not to make types which would be non-destructible. That's simply an outgrowth of permitting =delete on any function. It's not design or intent; it simply is.
While there isn't much use to applying =delete to a destructor, there also isn't much use in having the specification to explicitly forbid its use on a destructor. And there certainly isn't much use in making =delete behave differently when applied to a destructor.
With this:
A a;
You will call the destructor upon exiting the scope (and you have deleted the destructor, hence the error). With this:
A *a = new A();
You simply don't call the destructor (because you never use delete). The memory is cleaned up at the programs completion, but you are essentially guaranteeing a memory leak.
There is no reason for c++ to disallow this behavior because this would create a very specific case to program into a compiler. For example, c++ doesn't disallow this:
int *p; *p = 5;
Even though this is obviously bad, and will always be bad. It is up to the programmer to ensure they don't do this.
There is no reason that you should delete your destructor because it is not a useful behavior.
In a multi-threaded environment, you may be sharing the non-destructed class between threads.
If the thread that allocates memory terminates, there is no reason a pointer to that allocated memory couldn't still be in use by another thread.
The standard implies that a constructed object with dynamic storage duration does not qualify for destructor invocation.
12.4 Destructors [class.dtor]
A destructor is invoked implicitly
— for a constructed object with static storage duration at program termination,
— for a constructed object with thread storage duration
at thread exit,
— for a constructed object with automatic
storage duration when the block in which an object is created
exits,
— for a constructed temporary object when its
lifetime ends.
We can see the benefits of this through a basic memory sharing example between threads:
#include <thread>
#include <iostream>
//shared object
struct A {
void say_hello(){ std::cout << ++value << '\n'; }
~A() = delete;
int value;
};
//two threads
void creator();
void user(A* a);
//the creator allocates memory,
//gives it to another thread (via pointer),
//and then ends gracefully.
//It does not attempt to implicitly call the destructor.
//Nor would we want it to for allocated memory we are sharing.
void creator(){
A* a = new A();
a->value = 0;
std::thread t(user, a);
t.detach();
}
//The user recieves a pointer to memory,
//and is free to continue using it
//despite the creator thread ending
void user(A* a){
while(1){
a->say_hello();
}
}
//main->creator->user
int main(){
std::thread t(creator);
while(1){}
}
I have come across some code which has horrified me.
Essentially it follows this pattern :
class Foo
{
public:
//default constructor
Foo(): x(0), ptr(nullptr)
{
//do nothing
}
//more interesting constructor
Foo( FooInitialiser& init): x(0), ptr(nullptr)
{
x = init.getX();
ptr = new int;
}
~Foo()
{
delete ptr;
}
private:
int x;
int* ptr;
};
void someFunction( FooInitialiser initialiser )
{
int numFoos = MAGIC_NUMBER;
Foo* fooArray = new Foo[numFoos]; //allocate an array of default constructed Foo's
for(int i = 0; i < numFoos; ++i)
{
new( fooArray+ i) Foo( initialiser ); //use placement new to initialise
}
//... do stuff
delete[] fooArray;
}
This code has been in the code base for years and it would seem has never caused a problem. It's obviously a bad idea since someone could change the default constructor to allocate not expecting the second construction. Simply replacing the second constructor with an equivalent initialisation method would seem the sensible thing to do. eg.
void Foo::initialise(FooInitialiser& init)
{
x = init.getX();
ptr = new int;
}
Although still subject to possible resource leaks, at least a defensive programmer might think to check for prior allocations in a normal method.
My question is:
Is constructing twice like this actually undefined behaviour/ outlawed by standard or simply just a bad idea? If undefined behaviour can you quote or point me to right place to look in the standard?
Generally, working with placement new in this way is not a good idea. Calling an initializer from the first new, or calling an initializer instead of placement new are both considered to be better form than the code you've provided.
However, in this case, the behaviour of calling placement new over an existing object is well defined.
A program may end the lifetime of any object by reusing the storage
which the object occupies or by explicitly calling the destructor for
an object of a class type with a non-trivial destructor. For an object
of a class type with a non-trivial destructor, the program is not
required to call the destructor explicitly before the storage which
the object occupies is reused or released; however, if there is no
explicit call to the destructor or if a delete-expression (5.3.5) is
not used to release the storage, the destructor shall not be
implicitly called and any program that depends on the side effects
produced by the destructor has undefined behavior.
So when this happens:
Foo* fooArray = new Foo[numFoos]; //allocate an array of default constructed Foo's
for(int i = 0; i < numFoos; ++i)
{
new( fooArray+ i) Foo( initialiser ); //use placement new to initialise
}
The placement new operation will end the lifetime of the Foo that was there, and create a new one in it's place. In many circumstances this could be bad, but given the way your destructor works, this will be fine.
Calling placement new on an existing object could be undefined behaviour, but it depends on the specific object.
This does not produce undefined behaviour, because you are not depending on the "side effects" produced by the destructor.
The only "side-effect" in the destructor of your object is to delete the contained int pointer, but in this case that object is never in a deletable state when placement new is called.
If it was possible for the contained int pointer to be equal to something other than nullptr and could possibly require deleting, then calling placement new over the existing object would invoke undefined behaviour.
struct Foo
{
Foo(int i)
{
ptr = new int(i);
}
~Foo()
{
delete ptr;
}
int* ptr;
};
int main()
{
{
Foo a(8);
Foo b(7);
a = b;
}
//Do other stuff
}
If I understand correctly, the compiler will automatically create an assignment operator member function for Foo. However, that just takes the value of ptr in b and puts it in a. The memory allocated by a originally seems lost. I could do a call a.~Foo(); before making the assignment, but I heard somewhere that you should rarely need to explicitly call a destructor. So let's say instead I write an assignment operator for Foo that deletes the int pointer of the left operand before assigning the r-value to the l-value. Like so:
Foo& operator=(const Foo& other)
{
//To handle self-assignment:
if (this != &other) {
delete this->ptr;
this->ptr = other.ptr;
}
return *this;
}
But if I do that, then when Foo a and Foo b go out of scope, don't both their destructors run, deleting the same pointer twice (since they both point to the same thing now)?
Edit:
If I understand Anders K correctly, this is the proper way to do it:
Foo& operator=(const Foo& other)
{
//To handle self-assignment:
if (this != &other) {
delete this->ptr;
//Clones the int
this->ptr = new int(*other.ptr);
}
return *this;
}
Now, a cloned the int that b pointed to, and sets its own pointer to it. Perhaps in this situation, the delete and new were not necessary because it just involves ints, but if the data member was not an int* but rather a Bar* or whatnot, a reallocation could be necessary.
Edit 2:
The best solution appears to be the copy-and-swap idiom.
Whether this leaks memory?
No it doesn't.
It seems most of the people have missed the point here. So here is a bit of clarification.
The initial response of "No it doesn't leak" in this answer was Incorrect but the solution that was and is suggested here is the only and the most appropriate solution to the problem.
The solution to your woes is:
Not use a pointer to integer member(int *) but to use just an integer (int), You don't really need dynamically allocated pointer member here. You can achieve the same functionality using an int as member.
Note that in C++ You should use new as little as possible.
If for some reason(which I can't see in the code sample) You can't do without dynamically allocated pointer member read on:
You need to follow the Rule of Three!
Why do you need to follow Rule of Three?
The Rule of Three states:
If your class needs either
a copy constructor,
an assignment operator,
or a destructor,
then it is likely to need all three of them.
Your class needs an explicit destructor of its own so it also needs an explicit copy constructor and copy assignment operator.
Since copy constructor and copy assignment operator for your class are implicit, they are implicitly public as well, Which means the class design allows to copy or assign objects of this class. The implicitly generated versions of these functions will only make a shallow copy of the dynamically allocated pointer member, this exposes your class to:
Memory Leaks &
Dangling pointers &
Potential Undefined Behavior of double deallocation
Which basically means you cannot make do with the implicitly generated versions, You need to provide your own overloaded versions and this is what Rule of Three says to begin with.
The explicitly provided overloads should make a deep copy of the allocated member and it thus prevents all your problems.
How to implement the Copy assignment operator correctly?
In this case the most efficient and optimized way of providing a copy assignment operator is by using:
copy-and-swap Idiom
#GManNickG's famous answer provides enough detail to explain the advantages it provides.
Suggestion:
Also, You are much better off using smart pointer as an class member rather than a raw pointer which burdens you with explicit memory management. A smart pointer will implicitly manage the memory for you. What kind of smart pointer to use depends on lifetime and ownership semantics intended for your member and you need to choose an appropriate smart pointer as per your requirement.
the normal way to handle this is to create a clone of the object the pointer points to, that is why it is important to have an assignment operator. when there is no assigment operator defined the default behavior is a memcpy which will cause a crash when both destructors try to delete the same object and a memory leak since the previous value ptr was pointing to in b will not be deleted.
Foo a
+-----+
a->ptr-> | |
+-----+
Foo b
+-----+
b->ptr-> | |
+-----+
a = b
+-----+
| |
+-----+
a->ptr
\ +-----+
b->ptr | |
+-----+
when a and b go out of scope delete will be called twice on the same object.
edit: as Benjamin/Als correctly pointed out the above is just referring to this particular example, see below in comments
The code as presented has Undefined Behavior. As such, if it leaks memory (as expected) then that is just one possible manifestation of the UB. It can also send an angry threatening letter to Barack Obama, or spew out red (or orange) nasal daemons, or do nothing, or act as if there was no memory leak, miraculously reclaiming the memory, or whatever.
Solution: instead of int*, use int, i.e.
struct Foo
{
Foo(int i): blah( i ) {}
int blah;
};
int main()
{
{
Foo a(8);
Foo b(7);
a = b;
}
//Do other stuff
}
That’s safer, shorter, far more efficient and far more clear.
No other solution presented for this question, beats the above on any objective measure.
#include <iostream>
#include <memory>
class test
{
private:
int x , y;
public:
test(int a , int b):x(a),y(b){}
void fun()
{
std::cout<< x<<" "<<y<<" "<<std::endl;
}
};
void show(std::auto_ptr<test> t1)
{
t1->fun();
}
int main()
{
show(new test(3,4));
}
I am getting a compilation error , please tell me what's wrong in this code?Thanks in advance.
Whenever you dynamically allocate an object, you should create a named smart pointer that immediately takes ownership of the object, then use that named smart pointer. For example,
std::auto_ptr<test> ptr(new test(3, 4));
show(ptr);
You cannot pass new test(3, 4) directly to the function because std::auto_ptr objects must be explicitly constructed; it would be quite unexpected if a smart pointer took ownership of an object implicitly.
That said, this is rather unusual anyway because when you call show(), the auto_ptr is copied and when auto_ptr is "copied," the original loses ownership and the copy gains ownership (so, after show() is called, you will find that ptr no longer has ownership of the object.
James explained how to resolve the problem.
But the reason auto_ptr was designed so that you can not do this:
show(new test(3,4));
Is because this is a bad idea.
Even this (if it were allowed):
show(std::auto_ptr<test>(new test(3,4)));
Would be a bad idea.
So you ask why.
Well in the normal situation not a big deal.
Bu what happens when you have a function that takes more than one parameter.
show2(std::auto_ptr<test>(new test(3,4)), SomeObject());
Now the standard gurantees that all parameters will be fully evaluated before the call (even the construction of the auto_ptr). But it does not guarantee the evaluation order nor that the evaluation will not be interleaved.
Thus it is possible for this evaluation:
// Phsedu code for parameter evaluation
test* tmp1 = new test(3,4);
SomeObject const& tmp2 = SomeObject();
std::auto_ptr<test> const& tmp3(tmp1);
call(tmp3, tmp1)
This order is bad. Because if the constructor of SomeObject throws an exception you will leak tmp1 as it has not yet been assigned to the std::auto_ptr.
This is why we auto_ptr was designed so that you have to give it a named variable.
std::auto_ptr<test> tmp3(new test(3,4));
SomeObject tmp1;
call(tmp3, tmp1)
Now if SomObject constructor throws the test object will be tidied up.