Можно ли оптимизировать умный указатель? - PullRequest
5 голосов
/ 14 апреля 2020

Запомните код

...
{
    int* p = new int(0);
    std::unique_ptr<int> q(p);
    ...
    // make use of 'p'
}
...

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

Ответы [ 2 ]

5 голосов
/ 14 апреля 2020

С правилом «как если» компилятору разрешено выполнять любую оптимизацию, если наблюдаемое поведение идентично.

Немедленное освобождение q / p не будет разрешено, поскольку тогда вы будете использовать висячий указатель.

Хотя он может вызывать деструктор до конца области действия:

{
    int* p = new int(0);
    std::unique_ptr<int> q(p);
    ...
    // make use of 'p'
    ...
    // No longer use of p (and q)
    ...
    // Ok, can delete p/q now (as long there are no observable behaviors changes)
    ...
}

Поскольку operator new / delete может быть изменен глобально, компилятору обычно не хватает информации (компоновщик хотя), поэтому рассмотрим их (потенциально) наблюдаемое поведение (как и любые внешние функции).

c ++ 14 допускает некоторые варианты / оптимизацию нового выражения, поэтому

{
    delete new int(42);
    int* p1 = new int(0);
    int* p2 = new int(0);
    std::unique_ptr<int> q2(p2);
    std::unique_ptr<int> q1(p1);
    ...
    // make use of 'p1'/p2
    ...
}

Может быть "заменённым" на

{
    // delete new int(42); // optimized out
    std::unique_ptr<int[]> qs{new int [] {0, 0}}; // only one allocation instead of 2
    int* p1 = q->get();
    int* p2 = q->get() + 1;
    ...
    // make use of 'p1'/p2
    ...
}
0 голосов
/ 20 апреля 2020

Я понял ответ на свой собственный вопрос:
В коде

{
    int* p = new int(0);
    std::unique_ptr<int> q(p);
    ...
    // HERE
}

деструктор q это гарантированно будет вызван при выходе из области видимости ( ЗДЕСЬ ).
Хотя компилятору разрешено выделять и освобождать регистры для переменных по своему усмотрению, деструктор гарантированно вызывается в определенном месте - выход из области видимости.
Откуда я знаю?
Потому что это то, что позволяет C ++ scope guard . Зачастую защитник области используется для освобождения мьютекса при выходе из области действия - это то, что должно быть гарантировано.

...