cat ./d.cpp #include <iostream> class S { public: S() = default; ~S() { i=10; } void foo() { this->~S(); } int getI() const { return i; } private: int i{0}; }; int main() { S s; do { std::cout << "Before foo: " << s.getI(); s.foo(); std::cout << "; After: " << s.getI() << std::endl; } while (false); return 0; } g++ -O0 ./d.cpp $./a.out Before foo: 0; After: 10 g++ -O3 ./d.cpp georgii@ltgscosvm:~/prj/test$./a.out Before foo: 0; After: 0 gcc --version gcc (GCC) 10.2.1 20210130 (Red Hat 10.2.1-11) Copyright (C) 2020 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. hostnamectl Static hostname: ltgscosvm.be-tse01.de Icon name: computer-vm Chassis: vm Machine ID: fb944a0ffb46449f9b639e589d00b598 Boot ID: 433e59a4db5c419a9081cc6968e4e590 Virtualization: microsoft Operating System: CentOS Linux 7 (Core) CPE OS Name: cpe:/o:centos:centos:7 Kernel: Linux 3.10.0-1160.45.1.el7.x86_64 Architecture: x86-64 uname -a Linux ltgscosvm.be-tse01.de 3.10.0-1160.45.1.el7.x86_64 #1 SMP Wed Oct 13 17:20:51 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
From experiments, I would guess that in case of -O3 the call to destructor was substituted by initializer: This works fine: cat ./d.cpp #include <iostream> class S { public: S() : i{1} {} ~S() { i=0; } void foo() { this->~S(); } int getI() const { return i; } private: int i{0}; }; int main() { S s; do { std::cout << "Before foo: " << s.getI(); s.foo(); std::cout << "; After: " << s.getI() << std::endl; } while (false); return 0; } g++ ./d.cpp $./a.out Before foo: 1; After: 0 $g++ -O3 ./d.cpp $./a.out Before foo: 1; After: 0 g++ --version g++ (GCC) 10.2.1 20210130 (Red Hat 10.2.1-11)
This is undefined code. The object is officially not live after you call the deconstructor so GCC is able to remove the store from the deconstructor as being dead.
(In reply to Andrew Pinski from comment #2) > This is undefined code. The object is officially not live after you call the > deconstructor so GCC is able to remove the store from the deconstructor as > being dead. I appreciate your reply. But this is confusing. The object was NOT released (de-allocated). In essence Destructor is just a function. Why the content of the class had been re-initialized? IMU: there should be not such obvious difference between optimized and non-optimized code thnx in advance.
(In reply to Georgii.Shagov from comment #3) > (In reply to Andrew Pinski from comment #2) > > This is undefined code. The object is officially not live after you call the > > deconstructor so GCC is able to remove the store from the deconstructor as > > being dead. > > I appreciate your reply. But this is confusing. The object was NOT released > (de-allocated). In essence Destructor is just a function. No, the destructor is not just a function in C++, it terminates the object being alive (not de-allocated though). You can then reuse the space for another object, either the same type or a different type by using the inplacement new. > Why the content of the class had been re-initialized? It was not re-initialized rather the store was removed. > IMU: there should be not such obvious difference between optimized and > non-optimized code why not, it is undefined code.
> No, the destructor is not just a function in C++, it terminates the object > being alive (not de-allocated though). You can then reuse the space for > another object, either the same type or a different type by using the > inplacement new. > > > Why the content of the class had been re-initialized? > > It was not re-initialized rather the store was removed. > > > IMU: there should be not such obvious difference between optimized and > > non-optimized code > > why not, it is undefined code. I really appreciate and value your Reply, Andrew. I have modified the code a little bit: #include <iostream> class S { public: S() = default; ~S() { i=10; } void foo() { this->~S(); } int getI() const { return i; } private: int i{100}; }; int main() { S s; do { std::cout << "Before foo: " << s.getI(); s.foo(); std::cout << "; After: " << s.getI() << std::endl; } while (false); return 0; } $g++ -O3 ./d.cpp $./a.out Before foo: 100; After: 0 $g++ -O0 ./d.cpp $./a.out Before foo: 100; After: 10 > No, the destructor is not just a function in C++, it terminates the object > being alive (not de-allocated though). I do understand your point, yet I would not feel comfortable in sharing one. - It is not clear: what function/code has reset S::i value to 0 after a Direct call to Destructor? - Why 0? :-? - In order to avoid misleading I have allocated the object on the stack as you can see. So, no-memory mgmt is not involved - Does it mean gcc implants some code, being called after Destructor call, resetting the content of the class Instance to 0? ::memset?? - Is there any agreement unto this? I would be happy to learn more about this. Thnx in advance, Yours truly, George
Accessing the member after the containing variable has been destructed is undefined behavior in C++, so when you do it, anything can happen, it can format your disk, do nothing, crash, ...
Also, this is an automatic (i.e. stack) variable, so the destructor will run at the end of the block, meaning it will be settled twice. So another form of undefined behaviour. The program is just broken, in multiple ways. You cannot expect sensible behaviour from garbage code.
(In reply to Jonathan Wakely from comment #7) > Also, this is an automatic (i.e. stack) variable, so the destructor will run > at the end of the block, meaning it will be settled twice. Sorry, auto-correct changed "destroyed" to "settled"