4

The code below ends up with free(): double free detected in tcache 2. I want to know the inner process the error happens through.

#include <string>

class test
{
private:
    std::string member;
public:
    test(const std::string & arg) { member = arg; };
};

int main()
{
    test T = test("test test test test");
    T.~test();
    return 0;
}

The error depends on the length of the given string; if you change "test test test test" to "test", the program runs fine, at least with g++ on my computer.

3 Answers3

7

std::string has an internal buffer for short strings to avoid memory allocations. If the string is short, then there is no memory to free and it works. If the string is long it tries to free the buffer each time you call the destructor.

Here is more information on the Short String Optimization: Meaning of acronym SSO in the context of std::string

Buddy
  • 10,120
  • 5
  • 37
  • 57
4

In your code test T is a local variable and local variables are deleted when they go out of scope. If you call T.~test() explicitly then it deletes the variable before it goes out of scope. But when the variable really goes out of scope, the destructor is called again.

Consider the example (taken from your code):

#include <string>
#include <iostream>
class test
{
private:
    std::string member;
public:
    test(const std::string & arg) { member = arg; };
    ~test()
    {
        std::cout << " calling destructor "<<std::endl;
    }
};

int main()
{
    test T = test("test test test test");
    //take the call away and you will still have the destructor call
    //T.~test();
    return 0;
}

If now, you call the destructor explicitly, then you get the destructor called twice (once explicitly and once when the variable goes out of scope)

#include <string>
#include <iostream>
class test
{
private:
    std::string member;
public:
    test(const std::string & arg) { member = arg; };
    ~test()
    {
        std::cout << " calling destructor "<<std::endl;
    }
};

int main()
{

    test T = test("test test test test");
    //causing a double call to the destructor
    T.~test();
    return 0;
}

The execution would be

 calling destructor 
 calling destructor 
free(): double free detected in tcache 2
Abandon

If instead, you have allocated a pointer on test with new, you then need to explicitly call the destructor otherwise, you will have memory leaks.

When excuting the code below, you see that the destructor does not get called even if your pointer goes out of scope.

int main()
{

    test* T = new test("test test test test");
    // in this case you will have no more destructor automatically
    //T.~test();
    return 0;
}

You need to deallocate the allocated memory explicitly by calling the destructor but not by using T.~test() because now T is not of type test but test*. You only need to call delete T like this:

int main()
{

    test* T = new test("test test test test");
    //You need to deallocate the allocated memory explicitly
    //T.~test();
    delete T;
    return 0;
}

And the destructor is called on the allocated memory

Pat. ANDRIA
  • 1,858
  • 1
  • 9
  • 20
1

Your program has undefined behaviour. That does not mean it has to error, the shorter string behaving differently is allowed.

The destructor is called a second time, when the lifetime of test ends at the } of main.

There are very few situations where calling the destructor of an object is appropriate.

Caleth
  • 35,377
  • 2
  • 31
  • 53