Re[9]: Жизнь после деструктора
От: Mystic Artifact  
Дата: 22.10.20 19:55
Оценка:
Здравствуйте, σ, Вы писали:

MA>>Да ничего компилятор не выбрасывает и выбросить не может.


σ>Анус ставишь?


Ознакомься наконец с устройством компилятора, а не выплескивай тут свою охинею.
Re[3]: Жизнь после деструктора
От: AleksandrN Россия  
Дата: 22.10.20 19:57
Оценка: 1 (1) +2
Здравствуйте, Lis182, Вы писали:

L>Здравствуйте, SaZ, Вы писали:


L> inline ~SFile()

L> {
L> if (hFile != INVALID_HANDLE_VALUE)
L> CloseHandle(hFile), hFile = INVALID_HANDLE_VALUE;
L> }

L>[/ccode]


L>Смысл в том, чтоб файл закрывался при выпрыгивания из функции в любом месте. Как не сложно видеть, файл закроется только если он не был закрыт. Но мне потребовалось закрывать этот файл посередине функции (потом этот файл переименовывался). Ну я и вставил вызов деструктора.


А почему не
void Close()
{
    if (hFile != INVALID_HANDLE_VALUE)
        CloseHandle(hFile), hFile = INVALID_HANDLE_VALUE;
}

~SFile()
{
    Close();
}


и вызывать Close(), если закрыть файл нужно ещё до разрушения объекта?
Re[10]: Жизнь после деструктора
От: Lis182 Россия  
Дата: 22.10.20 20:15
Оценка:
σ>>Анус ставишь?

MA> Ознакомься наконец с устройством компилятора, а не выплескивай тут свою охинею.


Это не мое Вопрос был про обязанность сохранять область памяти под объект до выхода из области видимости. Естественно, если оптимизатор много повыкидывает, но это на работоспособности сказываться не должно. Скажем еслиб деструктор вообще пустым был.
Re[4]: Жизнь после деструктора
От: Lis182 Россия  
Дата: 22.10.20 20:19
Оценка:
AN>и вызывать Close(), если закрыть файл нужно ещё до разрушения объекта?

Я примерно так и сделал. Но мне на будущее интересно знать, это ошибка компилятора, или таки в языке допустимо после деструктора портить объект.
Re[6]: Жизнь после деструктора
От: Lis182 Россия  
Дата: 22.10.20 20:23
Оценка:
A>Проблема в хреновом знании С++.
A>

    A>
  1. Повторный вызов деструктора для экземпляра класса является UB и его использование однозначно говорит об уровне познаний аффтара кода.

    Вот интерено, это где-то явно прописано?

    A>
  2. Есть два варианта вызова деструктора, если используется тот, который «Virtual call», то для его корректного выполнения требуется кое-что дополнительное, в сравнении с «non-virtual call».

    Какой виртуал.. В данном случае это или статичная функция, или вообще инлайново воткнутый код. Функция, которой передается 1 параметр.

    A>
Re[7]: Жизнь после деструктора
От: σ  
Дата: 22.10.20 20:26
Оценка: 9 (2) +3
A>>Проблема в хреновом знании С++.
A>>* Повторный вызов деструктора для экземпляра класса является UB и его использование однозначно говорит об уровне познаний аффтара кода.

L>Вот интерено, это где-то явно прописано?


http://eel.is/c++draft/class.dtor#19:
Once a destructor is invoked for an object, the object's lifetime ends; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended ([basic.life]).
[Example 2: If the destructor for an object with automatic storage duration is explicitly invoked, and the block is subsequently left in a manner that would ordinarily invoke implicit destruction of the object, the behavior is undefined. — end example]
Отредактировано 22.10.2020 20:27 σ . Предыдущая версия .
Re[5]: Жизнь после деструктора
От: AleksandrN Россия  
Дата: 22.10.20 20:27
Оценка:
Здравствуйте, Lis182, Вы писали:



AN>>и вызывать Close(), если закрыть файл нужно ещё до разрушения объекта?


L>Я примерно так и сделал. Но мне на будущее интересно знать, это ошибка компилятора, или таки в языке допустимо после деструктора портить объект.


С++ не запрещает стрелять себе по ногам.
Re[6]: Жизнь после деструктора
От: Lis182 Россия  
Дата: 22.10.20 20:45
Оценка: :)
AN>С++ не запрещает стрелять себе по ногам.

Я начинал с ассемблера (сначала pdp-11, потом ПСшный). Основные привычки не изменились. В данном случае, я считаю, что деструктор — это просто функция с неявной передачей this. На счет выделения памяти, я был уверен, что очистка памяти произойдет только при выходе из области видимости переменной (или delete, если она создавалась через new). Я не прав получается? Хотелось бы какую-нить ссылку на документацию, где или я прав, или, допустим, "поведение не определено"
Re[8]: Жизнь после деструктора
От: Lis182 Россия  
Дата: 22.10.20 20:48
Оценка:
σ>http://eel.is/c++draft/class.dtor#19:
σ>Once a destructor is invoked for an object, the object's lifetime ends; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended ([basic.life]).
σ>[Example 2: If the destructor for an object with automatic storage duration is explicitly invoked, and the block is subsequently left in a manner that would ordinarily invoke implicit destruction of the object, the behavior is undefined. — end example]

Спасибо большое В этом и заключался мой вопрос
Re[11]: Жизнь после деструктора
От: Mystic Artifact  
Дата: 22.10.20 21:59
Оценка:
Здравствуйте, Lis182, Вы писали:

MA>> Ознакомься наконец с устройством компилятора, а не выплескивай тут свою охинею.

L>Это не мое Вопрос был про обязанность сохранять область памяти под объект до выхода из области видимости. Естественно, если оптимизатор много повыкидывает, но это на работоспособности сказываться не должно. Скажем еслиб деструктор вообще пустым был.
Так я и не тебе же ответил про негатив.

Ну тебе уже ответили в принципе то, в плане, что твой юз-кейс формально является UB.

Другое дело, что, компилятор натуральным образом с этой ситуацией особо ничего не может поделать (вообще-то может, например генерировать ворнинг или ошибку, но это же не будет соответствовать духу патриотизма C++), и генерируется такой псевдокод:

stack obj = SMyStruct::SMyStruct();
obj.~SMyStruct();
obj.val = 123;
obj.~SMyStruct();


Как видишь, тут очень даже ясное время жизни и памяти и всего остального. И, лишь в результате оптимизаций методы могут быть "не выброшены", а, вырождены, в результате инлайнинга. Но, т.к. в реальной жизни, ты говоришь, что там у тебя вызов содержит CloseHandle, то он разумется никуда его не выбросит, кроме случая, если он докажет, что _handle == null, или, что плохо, если _handle == undef.

Но, в твоём коде _handle не будет undef, если фронтэнд не приложит к этому специальных усилий со своей стороны (например таких):
stack obj = SMyStruct::SMyStruct();
obj.~SMyStruct();

// чисто теоретически, здесь компилятор вправе подложить свинью
obj = undef;

obj.val = 123;     // UB
obj.~SMyStruct();  // UB


Но, ни gcc ни clang такого не делают, хотя бы потому, что оно и так еле шевелится, да и если бы всё было так просто, то на это просто банально генерировалась бы ошибка самим компилятором, и это ни разу не соответствует никакой логики.

По моему личному мнению, ты наступил или на какое-то другое UB, если ещё что-нибудь менял, или на баг.

--

В целом, вот такой код:
int my_function()
{
    SMyStruct obj(123); // от твоей отличается только тем, что содержит вызов close_handle, как ты описывал
    obj.~SMyStruct();

    SMyStruct2 obj2(999);  // подобная структура с close_handle2

    obj._handle = 456;

    return 0;
}


просто обязан (кому?) порождать следующий код:
my_function():
        push    rax
        mov     edi, 123
        call    close_handle(int)
        mov     edi, 999
        call    close_handle2(int)
        mov     edi, 456
        call    close_handle(int)
        xor     eax, eax
        pop     rcx
        ret



Буквоедством то про UB заниматься можно сколько угодно, но технически, требование стандарта невозможно обеспечить кроме как в очень простых случаях, представленных выше и то этой ахинеей никто не занимается.
Re[12]: Жизнь после деструктора
От: σ  
Дата: 22.10.20 22:06
Оценка: +1
MA> И, лишь в результате оптимизаций методы могут быть "не выброшены", а, вырождены, в результате инлайнинга. Но, т.к. в реальной жизни, ты говоришь, что там у тебя вызов содержит CloseHandle, то он разумется никуда его не выбросит

Ой-вей, ты вообще читать умеешь? Выбрасывается не вызов CloseHandle, а присвоение после него. Выбрасывается безо всяких инлайнингов. https://godbolt.org/z/9EYKK6
Отредактировано 22.10.2020 22:07 σ . Предыдущая версия .
Re[13]: Жизнь после деструктора
От: Mystic Artifact  
Дата: 22.10.20 22:23
Оценка:
Здравствуйте, σ, Вы писали:

σ>Ой-вей, ты вообще читать умеешь? Выбрасывается не вызов CloseHandle, а присвоение после него. Выбрасывается безо всяких инлайнингов. https://godbolt.org/z/9EYKK6

-fno-lifetime-dse , -flimetime-dse=N . Конкретно эта оптимизация считается довольно таки спорная. (Стоит ли объяснять что тут за этой фичей не хватает 2 тонны диагностик со стороны компилятора?)

PS: И да, про присвоение, прочитав всю ветку, я и позабыл.
Отредактировано 22.10.2020 22:40 Mystic Artifact . Предыдущая версия . Еще …
Отредактировано 22.10.2020 22:26 Mystic Artifact . Предыдущая версия .
Re: Жизнь после деструктора
От: Don Reba Канада https://stackoverflow.com/users/49329/don-reba
Дата: 22.10.20 22:39
Оценка: 1 (1) +1
Здравствуйте, Lis182, Вы писали:

L>
L>{
L>    SMyStruct obj;    // выделяем память (обычно на стеке) под фактически один int
L>    obj.~SMyStruct(); // Явно вызываем деструктор
L>    obj.val = 123;    // Это правомерно? Т.е. указатель стека обязан не увеличиваться на siseof(int) после предыдущей строки?
L>}                     // Тут в любом случае вызовется деструктор, которому передастся this. Собственно, вопрос тот же, обязан ли компилятор держать в памяти объект до этого места
L>


Стек работает не так, как написано в комментариях к коду. Указатель стека увеличивается на размер стека функции при её вызове и восстанавливается при завершении. Конструкторы и деструкторы на него никак не влияют.
Ce n'est que pour vous dire ce que je vous dis.
Re[2]: Жизнь после деструктора
От: Lis182 Россия  
Дата: 23.10.20 11:28
Оценка: -1
DR>Стек работает не так, как написано в комментариях к коду. Указатель стека увеличивается на размер стека функции при её вызове и восстанавливается при завершении. Конструкторы и деструкторы на него никак не влияют.

Влияет выделение памяти. Целиком на всю функцию стековый фрейм выделяется не всегда. x86-32/64 по разному, в линуксе и винде тоже по разному. Ну а вообще данный пример можно оптимизировать, чтоб вообще стек не трогать. Я так скажем образно выразился, как бывает под x86-32.
Re: Жизнь после деструктора
От: Шахтер Интернет  
Дата: 24.10.20 11:20
Оценка:
Здравствуйте, Lis182, Вы писали:

Вот такой код законен. Но, по-моему, вы собираетесь делать какую-то фигню.

struct SMyStruct // Простейшая структурка с одним полем
 {
    int val;     // Поле, чтоб структура не была совсем пустой

    SMyStruct() {}

    ~SMyStruct() // Деструктор
    {
        val = 321; // Что-то делаем (что именно не важно). Важно, что это действие можно повторять много раз, если под объект выделена память.
    }
 };

{
    SMyStruct obj;    // выделяем память (обычно на стеке) под фактически один int
    obj.~SMyStruct(); // Явно вызываем деструктор
    new(&obj) SMyStruct(); 
    obj.val = 123;    // Это правомерно? Т.е. указатель стека обязан не увеличиваться на siseof(int) после предыдущей строки?
}                     // Тут в любом случае вызовется деструктор, которому передастся this. Собственно, вопрос тот же, обязан ли компилятор держать в памяти объект до этого места


Кстати, менять в деструкторе поля не имеет смысла -- компилятор эти действия выбросит при оптимизации.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re: Жизнь после деструктора
От: Lis182 Россия  
Дата: 24.10.20 15:28
Оценка:
Здравствуйте, Lis182, Вы писали:

L>Добрый день! По моему опыту, тут много "формалистов" (в хорошем смысле) по поводу стандарта Си(++). Постараюсь максимально упростить вопрос, в реальности проблема немного сложнее. Приведем псевдокод, в котором собственно вопросы в комментариях.


L>

L>struct SMyStruct // Простейшая структурка с одним полем
L>{
L>    int val;     // Поле, чтоб структура не была совсем пустой
L>    ~SMyStruct() // Деструктор
L>    {
L>        val = 321; // Что-то делаем (что именно не важно). Важно, что это действие можно повторять много раз, если под объект выделена память.
L>    }
L>};

L>{
L>    SMyStruct obj;    // выделяем память (обычно на стеке) под фактически один int
L>    obj.~SMyStruct(); // Явно вызываем деструктор
L>    obj.val = 123;    // Это правомерно? Т.е. указатель стека обязан не увеличиваться на siseof(int) после предыдущей строки?
L>}                     // Тут в любом случае вызовется деструктор, которому передастся this. Собственно, вопрос тот же, обязан ли компилятор держать в памяти объект до этого места
L>


Спасибо большое всем участвовавшим в обсуждении. Сейчас я понял, что по стандарту, поведение программы в данном случае не определено, т.е. UB. Падало совершенно странно, и я не был уверен, что причина именно в этом. Помогали и другие изменения кода.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.