Override delete operator with empty implementation - c++

Using the delete operator on an object normally leads to two things: calling the object's destructor (and its virtual base destructors, if present) and freeing the memory afterwards.
If override the delete operator on a class giving it an empty implementation {}, the destructor will still be called, but the memory does not get freed.
Assuming the destructor is also empty, will the delete then have any effect or would it be safe to continue using the "deleted" object (i.e. is there undefined behaviour)?
struct Foo {
static void operator delete(void* ptr) {}
Foo() {}
~Foo() {}
void doSomething() { ... }
}
int main() {
Foo* foo = new Foo();
delete foo;
foo->doSomething(); // safe?
}
Not that this would make much sense as it is, but I'm investigating in a "deferred delete" (gc) mechanism where objects won't get deleted instantly when delete gets called but shortly afterwards.
Update
Referring to some answers that mention memory leaks: let's assume the overloaded delete operator is not empty, but does store its ptr argument in a (let's say static, for the sake of simplicity) set:
struct Foo {
static std::unordered_set<void*> deletedFoos;
static void operator delete(void* ptr) {
deletedFoos.insert(ptr);
}
Foo() {}
~Foo() {}
}
And this set gets cleaned up periodically:
for (void* ptr : Foo::deletedFoos) {
::operator delete(ptr);
}
Foo::deletedFoos.clear();

From n4296:
A destructor is invoked implicitly
(11.1) — for a constructed object with static storage duration (3.7.1)
at program termination (3.6.3),
(11.2) — for a constructed object with thread storage duration (3.7.2)
at thread exit,
(11.3) — for a constructed object with automatic storage duration
(3.7.3) when the block in which an object is created exits (6.7),
(11.4) — for a constructed temporary object when its lifetime ends
(12.2).
In each case, the context of the invocation is the context of the
construction of the object. A destructor is also invoked implicitly
through use of a delete-expression (5.3.5) for a constructed object
allocated by a new-expression (5.3.4); the context of the invocation
is the delete-expression. [ Note: An array of class type contains
several subobjects for each of which the destructor is invoked. —end
note ] A destructor can also be invoked explicitly.
Thus, the very use of delete expression that calls delete operator, you implicitly call destructor as well. Object's life ended, it's an undefined behavior what happens if you will call a method for that object.
#include <iostream>
struct Foo {
static void operator delete(void* ptr) {}
Foo() {}
~Foo() { std::cout << "Destructor called\n"; }
void doSomething() { std::cout << __PRETTY_FUNCTION__ << " called\n"; }
};
int main() {
Foo* foo = new Foo();
delete foo;
foo->doSomething();
// safe? No, an UB. Object's life is ended by delete expression.
}
Output:
Destructor called
void Foo::doSomething() called
used: gcc HEAD 8.0.0 20170809 with -O2
The question starts with assumption that redefining delete operator and behaviour of object would omit destruction of object. Redefining destructor of object itself will not redefine destructors of its fields.
In fact it won't exist anymore from semantics point of view. It will not deallocate memory, which might be a thing if object is stored in memory pool. But it would delete abstract 'soul' of object, so to say. Calling methods or accessing fields of object after that is UB.
In particular case, depending on operation system, that memory may stay forever allocated. Which is an unsafe behavior. It also unsafe to assume that compiler would generate sensible code. It may omit actions altogether.
Let me add some data to object:
struct Foo {
int a;
static void operator delete(void* ptr) {}
Foo(): a(5) {}
~Foo() { std::cout << "Destructor called\n"; }
void doSomething() { std::cout << __PRETTY_FUNCTION__ << "a = " << a << " called\n"; }
};
int main() {
Foo* foo = new Foo();
delete foo;
foo->doSomething(); // safe?
}
Output:
Destructor called
void Foo::doSomething() a= 566406056 called
Hm? We didn't initialized memory? Let's add same call before destruction.
int main() {
Foo* foo = new Foo();
foo->doSomething(); // safe!
delete foo;
foo->doSomething(); // safe?
}
Output here:
void Foo::doSomething() a= 5 called
Destructor called
void Foo::doSomething() a= 5 called
What? Of course, compiler just omitted initialization of a in first case. Could it be because class doesn't do anything else? In this case it is possible. But this:
struct Foo {
int a, b;
static void operator delete(void* ptr) {}
Foo(): a(5), b(10) {}
~Foo() { std::cout << "Destructor called\n"; }
void doSomething() { std::cout << __PRETTY_FUNCTION__ << " a= " << a << " called\n"; }
};
int main() {
Foo* foo = new Foo();
std::cout << __PRETTY_FUNCTION__ << " b= " << foo->b << "\n";
delete foo;
foo->doSomething(); // safe?
}
will generate similar undefined value:
int main() b= 10
Destructor called
void Foo::doSomething() a= 2017741736 called
Compiler had considered field a unused by the time of death of foo and thus "dead" without impact on further code. foo went down with all "hands" and none of them formally do exist anymore. Not to mention that on Windows, using MS compiler those programs would likely crash when Foo::doSomething() would try to revive the dead member. Placement new would allow us to play Dr.Frankenstein role:
#include <iostream>
#include <new>
struct Foo {
int a;
static void operator delete(void* ptr) {}
Foo() {std::cout << __PRETTY_FUNCTION__ << " a= " << a << " called\n"; }
Foo(int _a): a(_a) {std::cout << __PRETTY_FUNCTION__ << " a= " << a << " called\n"; }
~Foo() { std::cout << "Destructor called\n"; }
void doSomething() { std::cout << __PRETTY_FUNCTION__ << " a= " << a << " called\n"; }
};
int main() {
Foo* foo = new Foo(5);
foo->~Foo();
Foo *revenant = new(foo) Foo();
revenant->doSomething();
}
Output:
Foo::Foo(int) a= 5 called
Destructor called
Foo::Foo() a= 1873730472 called
void Foo::doSomething() a= 1873730472 called
Irregardless to wit if we call destructor or not, compilers are allowed to decide that revenant isn't same thing as original object, so we can't reuse old data, only allocated memory.
Curiously enough, while still performing UB, if we remove delete operator from Foo, that operation seem to work as expected with GCC. We do not call delete in this case, yet removal and addition of it changes compiler behavior, which, I believe, is an artifact of implementation.

From N4296 (~C++14):
3.8 Object lifetime [basic.life]
...
The lifetime of an object of type T ends when:
(1.3) — if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
(1.4) — the storage which the object occupies is reused or released.
Then:
12.4 Destructors [class.dtor]
...
A destructor is trivial if it is not user-provided and if:
(5.4) — the destructor is not virtual,
(5.5) — all of the direct base classes of its class have trivial destructors, and
(5.6) — for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.
Otherwise, the destructor is non-trivial.
So basically, for simple enough classes, this is safe, but if you have any kind of resource-owning classes involved it is not going to be legal.
Take care, though, that in your example your destructor is user-provided, hence non-trivial.

Whether your hack can work depends on the members of the class. The destructor will always call the destructors of the class members. If you have any members that are strings, vectors, or other objects with an 'active' destructor, these objects will be destroyed, even tough the memory allocated for the containing object is still allocated.

The main problem is that even if have empty dtor's, other background things, like the release of virtual method tables, will happen. My personal approach in such cases is to make a private dtor and use a member method (name it destroy if you want) for filtering the delete operator. For example
class A
{
bool _may_be_deleted;
public:
A(bool may_be_deleted)
: _may_be_deleted(may_be_deleted){;}
void allow_delete()
{
_prevent_delete = false;
}
static bool destroy(A*);
private:
virtual ~A(){;}
};
bool A::destroy(A *pA)
{
if(pA->_may_be_deleted)
{
delete pA;
return true;
}
return false;
}
int main(int argc, char* argv[])
{
A* pA = new A(false);
A::destroy(pA); //returns false and A is not deleted
pA->allow_delete();
A::destroy(pA); //Ok, now A is destroyed and returns true;
}
Hope that helps.

Related

Why does the base class pointer point to the pure virtual method in the base class instead of the overidden method in the derived class?

#include <iostream>
class A
{
public:
virtual ~A() = default;
virtual void foo(void) = 0;
};
class B : public A
{
private:
int x;
public:
B(int a) : x(a) {}
void foo(void) { std::cout << "B: " << x << "\n"; }
};
class Foo
{
private:
A* a_ptr;
public:
Foo (B& x) { a_ptr = &x; }
A* get_ptr(void) { return a_ptr; }
void dummy(void) { std::cout << "Foo: "; std::cout << a_ptr << "\t "<< typeid(*a_ptr).name() << "\n"; a_ptr->foo(); std::cout << "\n"; }
};
int main(void)
{
B b(10);
Foo f(b);
f.dummy();
return 0;
}
If the constructor of Foo takes a reference to an object of B, then this program executes the way I expect it to, i.e. a_ptr->foo() calls B::foo().
However, if the constructor is changed to accept the parameter by value, then a_ptr->foo() resolves to A::foo(), and results in a pure virtual method called exception
Sample output (Passed by reference:):
Foo: 0x7fffe90a24e0 1B
B: 10
Sample output (Passed by value):
Foo: 0x7fffc6bbab20 1A
pure virtual method called
terminate called without an active exception
Aborted (core dumped)
I've a vague hunch as to why this might be happening, and I'm looking for some literature or reference which might prove or disprove my hypothesis: When passed by reference, the base class pointer a_ptr points to an entity whose lifetime exceeds past the call to a_ptr->foo().
However, when passed by value, a_ptr points to a temporary which is lost when the constructor exits.
I suppose this has something to do with the VTABLE of A, but I can't quite put my finger on it.
Yes, your suspicion is correct.
When the B object is passed by value into the Foo constructor, it becomes a local variable of the constructor. The constructor is saving a pointer to that local object, which goes out of scope when the constructor exits.
So, the call to a_ptr->foo() in Foo::dummy() is actually undefined behavior since a_ptr doesn't even point at a valid object to begin with. But, it doesn't really crash since A::foo() doesn't use its this pointer for anything. It just points to a compiler-defined function that throws the pure virtual method called error, which you don't catch, so your program terminates.
You assigned temporary object B by reference to a_ptr which is of type A*. On constructor exit this temporary object has beed destroyed. As VTABLE has been destroyed too, called A::foo, which is pure virtual. So you got it.

Order of static destructors

If a class Foo has a static member variable Bar, I would expect Bar's destructor to run only after the last instance of Foo's destructor runs. This doesn't happen with the code snippet below (gcc 6.3, clang 3.8):
#include <memory>
#include <iostream>
class Foo;
static std::unique_ptr<Foo> foo;
struct Bar {
Bar() {
std::cout << "Bar()" << std::endl;
}
~Bar() {
std::cout << "~Bar()" << std::endl;
}
};
struct Foo {
Foo() {
std::cout << "Foo()" << std::endl;
}
~Foo() {
std::cout << "~Foo()" << std::endl;
}
static Bar bar;
};
Bar Foo::bar;
int main(int argc, char **argv) {
foo = std::make_unique<Foo>();
}
Outputs:
Bar()
Foo()
~Bar()
~Foo()
Why is the order of destruction not the reverse of construction here?
If ~Foo() uses Foo::bar this is a use after delete.
In C++ the objects are constructed in order of occurrence and destructed in the reverse order. First comes foo construction, then bar construction then main is executed then bar is destructed and then foo. This is the behavior you are seeing. The switch appears because the constructor of foo doesn't construct a Foo, it constructs an empty unique_ptr, so you don't get to see Foo() in the output. Then bar is constructed with the output and in main you create the actual Foo after foo has long been constructed.
I would expect Bar's destructor to run only after the last instance of Foo's destructor runs.
No, as a static data member, Foo::bar is independent of any instances of Foo.
And for the code you showed,
static std::unique_ptr<Foo> foo; // no Foo created here
Bar Foo::bar; // Foo::bar is initialized before main(), => "Bar()"
int main(int argc, char **argv) {
foo = std::make_unique<Foo>(); // an instance of Foo is created, => "Foo()"
}
// objects are destroyed in the reverse order how they're declared
// Foo::bar is defined after foo, so it's destroyed at first => "~Bar()"
// foo is destroyed; the instance of Foo managed by it is destroyed too => "~Foo()"
The complication here is that the code doesn’t instrument the constructor of foo. What happens is that foo gets constructed first, than Foo::bar gets constructed. The call to make…unique constructs a Foo object. Then main exits, and the two static objects get destroyed in reverse order of their construction: Foo::bar gets destroyed, then foo. The destructor for foo destroys the Foo object that it points to, which is the one created inmain.
Static objects' lifetimes are based solely on the order of their definition. The compiler doesn't "know enough" when to call Bar::Bar() as much as calling Bar::~Bar().
To illustrate the problem better, consider this
class Foo;
struct Bar {
Bar() {
std::cout << "Bar()" << std::endl;
}
~Bar() {
std::cout << "~Bar()" << std::endl;
}
void baz() {}
};
struct Foo {
Foo() {
bar.baz();
std::cout << "Foo()" << std::endl;
}
~Foo() {
std::cout << "~Foo()" << std::endl;
}
static Bar bar;
};
Foo foo;
Bar Foo::bar;
int main() {}
Prints
Foo()
Bar()
~Bar()
~Foo()
The addition of std::unique_ptr postpones Foo::Foo() after its construction in main, giving the illusion of the compiler "knowing" when to call Bar::Bar().
TLDR Static objects should be defined later than its dependencies. Before defining bar, it is just as much a bug to define a std::unique_ptr<Foo> and to define a Foo
In general, you should not write code which depends on the order of construction or destruction of static (or global) data. This makes unreadable and unmaintainable code (you might prefer static smart pointers, or
explicit initialization or startup routines called from main). And AFAIK that order is not specified when you link several translation units.
Notice that GCC provides the init_priority and constructor (with priority) attributes. I believe you should rather avoid using them. However, the __attribute__(constructor) is useful inside plugins, for plugin initialization.
In some cases, you might also use atexit(3) (at least on POSIX systems). I don't know if such registered functions are called before or after destructors (and I think you should not care about that order).

Is destructor a normal function call?

Lets say I have two simple classes like this with non-virtual destructors:
struct A
{
~A() { std::cout << "A destructor" << std::endl; }
}
struct B : A
{
~B() { std::cout << "B destructor" << std::endl; }
}
When an instance of B is destructed, the destructor of A is called as well. When I destruct an instance of B through a pointer of type A* then Bs destructor will not be called. Does this also count for explicitly calling the destructor in a way you would also call a normal member function?
struct A
{
~A() { std::cout << "Hello" << std::endl; }
void f() { std::cout << "Hello" << std::endl; }
}
A a;
a.~A(); // case 1
a.f(); // case 2
In this example, is there any difference between the two cases other than the name of the function that is called?
EDIT: Consider the same example with CRTP:
template <typename C>
struct A
{
~A() { static_cast<C*>(this)->~C(); }
}
struct B : public A<B>
{
~B() { std::cout << "B destructor" << std::endl; }
}
A<B>* a = new B();
delete a;
Would this cause undefined behaviour or a memory leak?
If you want to explicitly call a destructor, it has to be called from an object of the same type as the destructor being called. Otherwise, this will yield undefined behavior:
In an explicit destructor call, the destructor name appears as a ~
followed by a type-name or decltypespecifier that denotes the
destructor’s class type. The invocation of a destructor is subject to
the usual rules for member functions (9.3); that is, if the object is
not of the destructor’s class type and not of a class derived from the
destructor’s class type (including when the destructor is invoked via
a null pointer value), the program has undefined behavior.
Also note that if explicitly calling a destructor, at the end of the scope where the instance resides, the destructor will implicitly be called a second time, provided that the object has been instanciated on the stack. That also yields undefined behavior:
Once a destructor is invoked for an object, the object no longer
exists; the behavior is undefined if the destructor is invoked for an
object whose lifetime has ended (3.8). [ Example: if the destructor
for an automatic object is explicitly invoked, and the block is
subsequently left in a manner that would ordinarily invoke implicit
destruction of the object, the behavior is undefined
I suggest you read section 12.4 of the spec. The working draft is freely available.
In fact, there are cases (i.e.: when using pools to avoid constant memory allocation), where you would normally call the destructor as a normal function, otherwise it would lead to undefined behaviour when you try to use placement new again on that memory address.
T* Pool::alloc() {
...
return new (memory) T();
}
void Pool::dealloc( T* memory ) {
...
memory->~T();
}
Regarding to the non virtual destructor issue, if the destructor is not virtual, deleting a B object inside a pointer to A, would lead to undefined behaviour.
struct A
{
~A() { std::cout << "A destructor" << std::endl; }
};
struct B : A
{
~B() { std::cout << "B destructor" << std::endl; }
};
A* a = new B();
delete a; // undefined behaviour, just ~A() will be called
B* b = new B();
delete b; // it's OK because b is B*
More info in this FAQ

Copy constructor for class with shared_ptr data members?

I know how to write a copy constructor when you have raw pointer data members in a class, but how do you write a copy constructor when you manage these with a shared_ptr?
Is there a copy() or clone() function which should be called? Interestingly I have never seen such an example.
The copy constructor for std::shared_ptr creates a second pointer which shared ownership with the first pointer. The pointee will be destroyed when all std::shared_ptr that point to it are destroyed.
Without a copy constructor explicitly declared, the language specification decrees that a suitable one will be implicitly provided, and that it will call the copy constructors of each member of your class.
So, in other words, the implicit copy constructor provided for your class will call shared_ptr<T>::shared_ptr(const shared_ptr<T> &), which will create a second share pointer pointing to the same object, and it doesn't look like that's what you want. If you want a deep copy of the pointee, you will have to declare your own copy constructor that creates one:
class Foo
{
Foo(const Foo &other)
: foobar(new Bar(*other.foobar.get()))
{}
shared_ptr<Bar> foobar;
}
References:
std::shared_ptr constructor documentation
Copy constructor semantics in C++98 (see section 12.8.8) and C++11 (see section 12.8.16)
std::shared_ptr has its own copy constructor that will handle the reference counting of the managed shared data.
struct Foo {
Foo(int i) : ptr{std::make_shared<int>(i)} {}
std::shared_ptr<int> ptr;
};
int main() {
Foo f1{0};
Foo f2{f1}; // Using the implicitly generated copy ctor.
*f1.ptr = 1;
std::cout << *f1.ptr << std::endl; // 1 (data is shared).
std::cout << *f2.ptr << std::endl; // 1 (data is shared).
}
If you want your class to have resource ownership semantics then simply declare the resource as an object with automatic storage duration.
struct Foo2 {
Foo2(int i) : i{i} {}
int i; // Automatic object.
};
int main() {
Foo2 f1{0};
Foo2 f2{f1};
f1.i = 1;
std::cout << f1.i << std::endl; // 1
std::cout << f2.i << std::endl; // 0
}

Behaviour of explicit call to destructor

The definition of some_class is:
class some_class
{
// stuff
public:
~some_class()
{
delete dynamic_three;
}
private:
classA one;
classB two;
classC* dynamic_three;
}
When the lifetime of a object ends, its destruction is: (1) to call its destructor and (2) to destroy its subobjects in the same order on which they are declared in the class definition (= position in memory).
But, if I have something like that:
auto* ptr = new some_class();
// more stuff
ptr->~some_class(); // l. X
The step (2) is also realized? I mean, in line X, are destructors of subobjects also called or only is executed the body of the some_class's destructor?
The step (2) is also realized?
Yes, it’s guaranteed. The behaviour is safe, but note that in your case you have no way of safely reclaiming the memory of the object without re-constructing it first via placement-new (unless you overladed operator new for your object and you can thus guarantee how the memory was allocated and use the matching deallocation).
Let's make a test:
class classA
{
public:
~classA() { cout << "~classA()" << endl; }
};
class classB
{
public:
~classB() { cout << "~classB()" << endl; }
};
class some_class
{
public:
~some_class() { cout << "~some_class()" << endl; }
private:
classA one;
classB two;
};
int main()
{
cout << "Start..." << endl;
auto* ptr = new some_class();
ptr->~some_class();
cout << "End." << endl;
}
Output:
Start...
~some_class()
~classB()
~classA()
End.
So the all destructors are called and in reverse order.
When the lifetime of a object ends, its destruction is: (1) to call its destructor and (2) to destroy its subobjects in the same order on which they are declared in the class definition (= position in memory).
and (3) the allocated memory is freed.
The step (2) is also realized?
Step (2) yes, but not step (3).
But if you can write
auto* ptr = new some_class();
note that you can also write
std::unique_ptr<ptr> ptr (new some_class());
which would call delete for you (of course, only use this if this matches your needs, but use this by default if you are not sure).