Tim Baker
Tim Baker

Reputation: 23

Templated Manual Destructor Call Not Working

I want to destroy the templated object, but keep the allocated memory to be filled. Unfortunately, the destructor of the object is never called, and stepping through the code, it skips the manual call.

#include <iostream>

template <typename type> class TestClass
{
    private:

        type *data;

    public:

        TestClass();
        ~TestClass();

        template <typename T> void Set(T &&element);
        void Replace();
};


template <typename type> TestClass<type>::TestClass()
{
    data = reinterpret_cast<type *>(new char[sizeof(type)]);;
}

template <typename type> TestClass<type>::~TestClass()
{
}

template <typename type> template <typename T> void TestClass<type>::Set(T &&element)
{
    new(data) type(static_cast<T &&>(element));
}

template <typename type> void TestClass<type>::Replace()
{
    type *pointer = reinterpret_cast<type *>(&data[0]);
    pointer->~type();

    //Fill with data
}

class MyClass
{
    public:

        MyClass()
        {
        }

        ~MyClass()
        {
            std::cout << "Called" << "\n";
        }
};

int main()
{
    MyClass *myClass = new MyClass();

    TestClass<MyClass *> myObject;

    myObject.Set(myClass);
    myObject.Replace();

    return 0;
}

I have tested this in VS 2017 and on an online C++ compiler. Both skip the pointer->~type(); when stepping through and the destructor is never called.

Edit: Rewrote code which now reproduces the error.

Upvotes: 0

Views: 60

Answers (2)

aschepler
aschepler

Reputation: 72291

To allow templates to deal generally with types, C++ allows a obj.~type() or ptr->~type() syntax even when the type is not a class type (but not for an array type). The meaning is the same as whatever would happen to an automatic object of that type at the end of its scope: if it is a class type, the destructor is called, and if not, nothing happens. For the case when the type is not a class type, this syntax is called a pseudo-destructor.

Now looking at your example, you're using the class template specialization TestClass<MyClass*>. So in the instantiation of the member definition for TestClass<type>::Replace(), type is an alias for MyClass*. The statement

type *pointer = reinterpret_cast<type *>(&data[0]);

defines a variable of type type*, which is MyClass**. (The right-hand side is confusing: &data[0] is the same as data assuming it points at something, and both expressions already have type type*.)

The statement

pointer->~type();

says to destroy the object of type type which pointer points at. That is, it says to destroy the object of type MyClass* which is *pointer. MyClass* is not a class type; it is a pointer type. So this is a call to a pseudo-destructor, and absolutely nothing happens.

There's not enough context to say for certain how this would be fixed, but perhaps you need to use TestClass<MyClass> instead of TestClass<MyClass*>? (Also, in real code don't forget the Rule Of Five/Rule Of Three.)

Upvotes: 0

NewMe
NewMe

Reputation: 162

It does call destructor.

#include <iostream>

class Type
{
    public:
    ~Type()
    {
        std::cout<< __FUNCTION__ << "\n";
    }
};


template <typename type> class MyClass
{
    private:

        type *data;

    public:

        MyClass();
        ~MyClass(){}

        void Replace();
};


template <typename type> MyClass<type>::MyClass()
{
    data =  reinterpret_cast<type *>(new char[sizeof(type)]);;
}


template <typename type> void MyClass<type>::Replace()
{
    type *pointer = &data[0];
    pointer->~type();

    //Fill with replacement data
}
int main()
{

    MyClass<Type> myClass;

    std::cout <<"Before destruction\n";
myClass.Replace();
std::cout << "After destruction\n";
    return 0;

}

Upvotes: 1

Related Questions