free (): двойное освобождение обнаружено в tcache 2 в C ++ - PullRequest
3 голосов
/ 21 сентября 2019

Во-первых, я действительно проверил, есть ли уже заданный вопрос, но я не смог его найти.Сообщение об ошибке не должно вводить вас в заблуждение. Моя ситуация немного отличается, я думаю, или я просто что-то упускаю.

Пока я работал с игрушечным кодом C ++, я столкнулся со странной ошибкой.Вывод программы говорит о наличии двойной свободной ситуации, но я не вижу места, где происходит эта ошибка.Код может быть немного длинным, я извиняюсь за это.

Я сейчас работаю над Linux Distribution и использую g++ 9.1.0.Я проверил свой код и искал ошибочную часть.

Несмотря на то, что я исправил некоторую часть кода, моя проблема не была решена, за исключением случаев, когда я комментировал либо Foo{1, "Hello World"}; или vec.push_back(std::move(Foo{}));, и я не понимаю, почему.

class Foo
{
public:
    Foo()
        : val{nullptr}, str{nullptr}
    {
        std::cout << "You are in empty constructor\n";
    }

    Foo(int the_val, const char *the_str)
        : val{new int}, str{new char[std::strlen(the_str + 1)]}
    {
        *val = the_val;
        std::cout << *val << '\n';
        std::strcpy(str, the_str);
        std::cout << str << '\n';
    }

    ~Foo()
    {
        if (val) {
            delete val;
        } else {
            std::cout << "val is empty\n";
        }

        if (str) {
            delete[] str;
        } else {
            std::cout << "str is empty\n";
        }
    }

    Foo(const Foo&) = delete;
    Foo& operator= (const Foo&) = delete;

    Foo(Foo&& rhs)
    {
        std::cout << "Move constructor is triggered\n";

        if (val) {
            delete val;
        }
        val = rhs.val;
        rhs.val = nullptr;

        if (str) {
            delete[] str;
        }
        str = rhs.str;
        rhs.str = nullptr;
    }

    Foo& operator= (Foo& rhs)
    {
        std::cout << "Move assignment is triggered\n";

        // Self-assignment detection
        if (&rhs == this) {
            return *this;
        }

        if (val) {
            delete val;
        }
        val = rhs.val;
        rhs.val = nullptr;

        if (str) {
            delete[] str;
        }
        str = rhs.str;
        rhs.str = nullptr;

        return *this;
    }
private:
    int *val;
    char *str;
};


int main()
{
    Foo{1, "Hello World"};

    std::vector<Foo> vec;
    vec.push_back(std::move(Foo{}));

    return 0;
}

Если я не прокомментирую какое-либо место в функции main, вывод будет следующим:

1
Hello World
You are in empty constructor
val is empty
str is empty
You are in empty constructor
Move constructor is triggered
free(): double free detected in tcache 2
Aborted (core dumped)

Если я прокомментирую "Foo {1," Hello World "};", вывод станет

You are in empty constructor
Move constructor is triggered
val is empty
str is empty
val is empty
str is empty

и, наконец, когда я комментирую "vec.push_back (std :: move (Foo {}));", вывод становится

You are in empty constructor
Move constructor is triggered
val is empty
str is empty
val is empty
str is empty

1 Ответ

4 голосов
/ 21 сентября 2019

Для начала этот конструктор использует неверный инициализатор mem

Foo(int the_val, const char *the_str)
    : val{new int}, str{new char[std::strlen(the_str + 1)]}
                                             ^^^^^^^^^^^

Я думаю, вы имеете в виду

Foo(int the_val, const char *the_str)
    : val{new int}, str{new char[std::strlen(the_str  ) + 1]}

Этот конструктор перемещения также недопустим

Foo(Foo&& rhs)
{
    std::cout << "Move constructor is triggered\n";

    if (val) {
        delete val;
    }
    val = rhs.val;
    rhs.val = nullptr;

    if (str) {
        delete[] str;
    }
    str = rhs.str;
    rhs.str = nullptr;
}

Внутри тела конструктора члены данных val и str имеют неопределенные значения.Они не были инициализированы, когда тело конструктора получило управление.

Вы могли бы написать это следующим образом

Foo(Foo&& rhs) : val( nullptr ), str( nullptr )
{
    std::cout << "Move constructor is triggered\n";

    std::swap( val, rhs.val );
    std::swap( str, rhs.str );
}

Этот оператор

Foo& operator= (Foo& rhs)

не являетсяоператор перемещения-назначения.Это оператор присваивания копии.Так что его определение неверно.

Также это утверждение в main

Foo{1, "Hello World"};

не имеет смысла.Объект создается и сразу удаляется.

В этом операторе

vec.push_back(std::move(Foo{}));

std::move является избыточным, поскольку Foo{} уже является значением.

...