Потоковая безопасность с C ++ 11 и передача по ссылке с временным объектом - PullRequest
2 голосов
/ 24 апреля 2020

Прочитав ответ в Аналогичный вопрос , я все еще размышляю, почему этот фрагмент кода не получает ошибку сегмента. Временные объекты «данные» должны быть освобождены после выхода из области видимости. Но, видимо, «данные» не публикуются. Кто-нибудь может мне помочь? Спасибо

#include <iostream>
#include <string>
#include <thread>
using namespace std;

void thfunc(string &data)
{
    for (;;)
    {
        cout << data << endl;
    }
}

int main()
{
    {
        string data = "123";
        std::thread th1(thfunc, std::ref(data));
        th1.detach();
    }

    for (;;)
    {
        cout << "main loop" << endl;
    }

    return 0;
}

Аналогичный вопрос

1 Ответ

7 голосов
/ 24 апреля 2020

Временный объект освобождается по стандарту, что приводит к неопределенному поведению . Реализация может делать все что угодно, в том числе хранить байты объекта в стековой памяти до тех пор, пока они не будут перезаписаны, что позволяет вашему коду (некорректно) работать.

Когда я разобрал двоичный файл, созданный моим компилятором (clang ++ 9.0.1) Я заметил, что указатель стека не «регрессировал», когда блок, содержащий data, закончился, таким образом предотвращая его перезапись, когда cout << "main loop" << endl; приводил к вызовам функций.

Кроме того, из-за короткой строки При оптимизации фактический ASCII "123" хранится в самом объекте std::string, а не в буфере, выделенном для кучи.

Проект чернового стандарта гласит следующее:

6.6.4.3 Автоматическое c срок хранения

Переменные области действия блока, явно не объявленные как stati c, thread_local или extern, имеют автоматическое c хранилище продолжительность. Хранение этих сущностей длится до тех пор, пока блок, в котором они созданы, не выйдет из системы .

Экспериментально, если я сделаю строку достаточно длинной, чтобы отключить оптимизацию короткой строки, программа все еще молча работает, поскольку байты в буфере остаются неизменными в моем эксперименте . Если я включаю ASAN, я получаю правильное предупреждение об использовании кучи после освобождения, потому что байты были освобождены в конце времени жизни строки, но к ним обращались через недопустимое использование указателя на теперь разрушенную строку.

...