-9 c++ gcc destructor
最近我在直接调用析构函数中发现了一个奇怪的行为,即值得一提的 IMU。
考虑下面的简单代码:
#include <iostream>
class S {
public:
S() = default;
~S() { i=10; }
int i{100};
};
int main()
{
S s;
do {
std::cout << "Before foo: " << s.i;
s.~S();
std::cout << "; After: " << s.i << std::endl;
} while (false);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
跑步:
$g++ -O3 ./d.cpp -o d
$./d
Before foo: 100; After: 0
$g++ -O0 ./d.cpp -o d
$./d
Before foo: 100; After: 10
Run Code Online (Sandbox Code Playgroud)
IMU:这非常令人困惑。由于某种原因,对析构函数的直接调用似乎被优化掉了,即被忽略了。为什么?
我试图向 gcc 社区呼吁,但对他们的回复感到非常惊讶:gcc-bug-103843
他们无法理解案件,推诿和开玩笑,使环境适得其反,我不准备继续下去。
我做了一些额外的调查:
#include <iostream>
class S {
public:
static constexpr size_t s_len=10;
public:
S() { for(auto i=0; i<s_len; ++i) m_arr[i]=10; }
~S() { for(auto i=0; i<s_len; ++i) m_arr[i]=11; /*prnt(); std::cout << std::endl;*/ }
void prnt() { for(auto i=0; i<s_len; ++i) std::cout << m_arr[i] << "; "; }
public:
int m_arr[s_len];
};
int main()
{
S s;
do {
std::cout << "Before foo: "; s.prnt();
s.~S();
std::cout << "; After: "; s.prnt();
std::cout << std::endl;
} while (false);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
跑步:
$g++ -O0 ./d2.cpp -o d2
$./d2
Before foo: 10; 10; 10; 10; 10; 10; 10; 10; 10; 10; ; After: 11; 11; 11; 11; 11; 11; 11; 11; 11; 11;
$g++ -O3 ./d2.cpp -o d2
$./d2
Before foo: 10; 10; 10; 10; 10; 10; 10; 10; 10; 10; ; After: 10; 10; 10; 10; 10; 10; 10; 10; 10; 10;
Run Code Online (Sandbox Code Playgroud)
这更令人困惑。
还有一项测试:
#include <iostream>
class S {
public:
static constexpr size_t s_len=10;
public:
S() { for(auto i=0; i<s_len; ++i) m_arr[i]=10; }
~S() { for(auto i=0; i<s_len; ++i) m_arr[i]=11; prnt(); std::cout << std::endl; }
void prnt() { for(auto i=0; i<s_len; ++i) std::cout << m_arr[i] << "; "; }
public:
int m_arr[s_len];
};
int main()
{
S s;
do {
std::cout << "Before foo: "; s.prnt();
s.~S();
std::cout << "; After: "; s.prnt();
std::cout << std::endl;
} while (false);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
跑步:
$g++ -O0 ./d2.cpp -o d2
$./d2
Before foo: 10; 10; 10; 10; 10; 10; 10; 10; 10; 10; 11; 11; 11; 11; 11; 11; 11; 11; 11; 11;
; After: 11; 11; 11; 11; 11; 11; 11; 11; 11; 11;
11; 11; 11; 11; 11; 11; 11; 11; 11; 11;
$g++ -O3 ./d2.cpp -o d2
$./d2
Before foo: 10; 10; 10; 10; 10; 10; 10; 10; 10; 10; 11; 11; 11; 11; 11; 11; 11; 11; 11; 11;
; After: 11; 11; 11; 11; 11; 11; 11; 11; 11; 11;
11; 11; 11; 11; 11; 11; 11; 11; 11; 11;
Run Code Online (Sandbox Code Playgroud)
这是完全正确的。
从我看来,直接调用析构函数并不简单,并且有一些协议。在相同情况下(-O3),它的行为有所不同,此外,它与一个版本的编译器不同。
我想更好地了解这些协议是什么?他们可靠吗?谁可以帮忙?
g++ --version
g++ (GCC) 10.2.1 20210130 (Red Hat 10.2.1-11)
Run Code Online (Sandbox Code Playgroud)
提前谢谢乔治
在对象被破坏后使用它是未定义的行为,因此编译器没有义务确保您的代码将按您的预期工作。可以自由地假设i析构函数运行后的值并不重要,因此它可以相应地进行优化,甚至可能优化器根本就不是为了处理这种情况而编写的,这就是为什么您会看到以下结果0:可能是一些默认值。同样,这对于编译器来说是完全合法的事情,因为您的代码的行为是未定义的,您很幸运它不会崩溃。
您可以看到,这可能就是在禁用优化时您的代码“有效”这一事实所发生的情况。
| 归档时间: |
|
| 查看次数: |
121 次 |
| 最近记录: |