Reputation: 1659
I would like to ensure that an object's destructor does not run. Is there any way to do this other than putting the object on the heap and not calling delete?
Upvotes: 16
Views: 16775
Reputation: 381
I figured out a weird way to do this that is a similar to the optional example but truly leaks whatever you want.
template<typename T>
class Leaker
{
public:
union { T m_val; char dummy; };
template<typename... Args>
Leaker(Args... inputArgs) : m_val(inputArgs...) {}
~Leaker() { }
};
One limitation that I think is fundamental to C++ is that there is seemingly no way to leak a value that has been constructed normally on the stack, you have to construct it using the union trick.
BadAuto helper1() {
...
}
Leaker<BadAuto> helper2() {
}
void main() {
// Will still call destructor
Leaker<BadAuto> leak1(helper1());
// Will NOT call destructor
Leaker<BadAuto> leak2 = helper2();
}
This is very unfortunate in my opinion and I hope the C++ standards committee adds in a true equivalent to std::mem::forget from Rust which is truly general.
Upvotes: 1
Reputation: 3
I know this is 10 years old, but I needed to stop a destructor from executing recently (destructor unloaded a manually mapped bit of data from the binary, I needed that manually mapped bit of data to be persistent until the executable itself completed running for one instance of an object instantiated all over)
Easiest way was to add a public bool to the object and wrap the destructor in that bool. Strictly speaking does not 'prevent the destructor from running', but it does control the flow of the destructor which you do not typically see.
Class::~Class()
{
if (bPersisent)
{
Disable();
}
}
Upvotes: 0
Reputation: 2144
You can call new operator with preallocated memory and give pointer to local variable which is on stack so it will be living on stack allocated area:
void myfunc() {
char stackMemoryForMyClass[sizeof(Type)];
Type* t = new (stackMemoryForMyClass) Type;
// destructor will not be called
}
Upvotes: -1
Reputation: 1261
I'm surprised that nobody mentioned completely C++ standard compliant solution with union
. In union
no constructor even destructor is called automatically for members. Even in case there is only one member in the union. All that must be done "manually":
constructor can be called through so called "placement new" (or from a constructor init list introduced by :
),
destructor can be called by explicit call of destructor method.
Demo:
class MyObj {
int i;
public:
MyObj(int i_) : i(i_) { std::cout << i << " constructed" << std::endl; }
MyObj(MyObj const &src) : i(src.i + 100) { std::cout << src.i << " copied to new " << i << std::endl; }
~MyObj() { std::cout << i << " destroyed" << std::endl; }
};
class OptDestr {
bool callDestructor;
union { MyObj o; }; // Only allocated, but no constr/destr is called automatically
public:
// Constructor
OptDestr(int i, bool callDestructor_) : callDestructor(callDestructor_),
o(i) // calls MyObj constructor
{ }
// OR alternatively:
OptDestr(int i, bool callDestructor_) : callDestructor(callDestructor_) {
new (&o)MyObj(i); // placement new - does NOT allocate, just calls constructor
}
// Copy constructor
OptDestr(OptDestr const &src) : callDestructor(src.callDestructor),
o(src.o) // explicit call of MyObj copy-constructor
{ }
// OR alternatively:
OptDestr(OptDestr const &src) : callDestructor(src.callDestructor) {
new (&o)MyObj(src.o); // placement new - no allocation, just explicitly calls copy-constructor of MyObj
}
// Destructor
~OptDestr() {
if (callDestructor) o.~MyObj(); // explicit call of destructor
}
};
int main() {
OptDestr d1(1, false /*callDestructor*/);
OptDestr d1_copy(d1);
OptDestr d2(2, true /*callDestructor*/);
OptDestr d2_copy(d2);
}
The output of the program is the following:
1 constructed
1 copied to new 101
2 constructed
2 copied to new 102
102 destroyed
2 destroyed
You can see that there is no 1 destructed
neither 101 destructed
. I would guess that types like std::Optional
are implemented in a similar way.
Upvotes: 21
Reputation: 1047
Following how std::optional operates, you can create your own simpler object wrapper template:
#include <iostream>
template <typename T_>
struct Wrapper
{
union {
char dummy_;
T_ value_;
};
bool shouldDestroy_ {true};
template<typename ...Args>
Wrapper(Args &&... args) :
value_(std::forward<Args>(args)...)
{
}
const T_ &value() const { return value_; }
T_ &value() { return value_; }
const T_ *get() const { return &value_; }
T_ *get() { return &value_; }
const T_ *operator->() const { return get(); }
T_ *operator->() { return get(); }
void destroy(bool destroy) { shouldDestroy_ = destroy; }
~Wrapper()
{
if (shouldDestroy_) {
value_.~T_();
}
}
};
int main()
{
struct Foo
{
Foo() { std::cout << "Foo()\n"; }
Foo(int value) { std::cout << "Foo(" << value << ")\n"; }
~Foo() { std::cout << "~Foo()\n"; }
void display(const char *str) { std::cout << str << "\n"; }
};
std::cout << "Running...\n";
// example 1 construct/destructed
{
Wrapper<Foo> a;
a.value().display("via value 1");
a->display("via operator 1");
}
// example 2 construct/destructed
{
Wrapper<Foo> a(2);
a.value().display("via value 2");
a->display("via operator 2");
}
// example 3 construct NOT destructed
{
Wrapper<Foo> a(3);
a.value().display("via value 3");
a->display("via operator 3");
// do not allow destroy
a.destroy(false);
}
return 0;
}
And the output will be as follows:
Running...
Foo()
via value 1
via operator 1
~Foo()
Foo(2)
via value 2
via operator 2
~Foo()
Foo(3)
via value 3
via operator 3
Upvotes: 3
Reputation: 283634
Not recommended, but here's another way:
char buffer[sizeof(T) + alignof(T)];
char* aligned_buffer = buffer + alignof(T) - reinterpret_cast<intptr_t>(buffer) % alignof(T);
T* object = new (aligned_buffer) T;
However, although this means the compiler won't automatically call the destructor, the programmer still can:
object->~T();
Of course, that's also true with heap allocation:
delete object;
To prevent that, you'll have to make the destructor inaccessible:
struct T
{
private:
~T() {};
};
Or really inaccessible (related: Why must a base class destructor be accessible only when a custom constructor is declared?):
class indestructible_base
{
~indestructible_base();
};
struct T : indestructible_base
{
};
Upvotes: 17
Reputation: 53047
If you want an error to occur upon calling the destructor then simply don't provide a definition for it:
struct foo {
~foo();
};
Or delete
it in C++11:
struct foo {
~foo() = delete;
};
If you only want it to be called sometimes then you almost certainly need to rethink your design. There's no "safe" way to do it, and while using new
without delete may work, I strongly suggest you don't.
Alternatively you could add a flag if you sometimes want certain destructor behavior:
struct foo {
foo(bool destroy = true) : destroy(destroy) {}
~foo() {
if(destroy) {
// destruction stuff here
}
}
bool destroy;
};
Upvotes: 2