C++ 变形多态对象
考虑以下代码片段:C++ 变形多态对象,c++,C++,考虑以下代码片段: #include <new> #include <iostream> struct IDivideResult { virtual int result() = 0; virtual int remainder() = 0; }; struct DivideResult : IDivideResult { DivideResult(int result, int remainder) : result_(result), r
#include <new>
#include <iostream>
struct IDivideResult {
virtual int result() = 0;
virtual int remainder() = 0;
};
struct DivideResult : IDivideResult {
DivideResult(int result, int remainder) : result_(result), remainder_(remainder) {}
int result() override { return result_; }
int remainder() override { return remainder_; }
int result_, remainder_;
};
struct LazyDivideResult : IDivideResult {
LazyDivideResult(int dividend, int divisor) : dividend_(dividend), divisor_(divisor) {}
int result() override { return Transmogrify()->result(); }
int remainder() override { return Transmogrify()->remainder(); }
DivideResult *Transmogrify() {
int result = dividend_ / divisor_;
int remainder = dividend_ % divisor_;
return new (this) DivideResult(result, remainder);
}
int dividend_, divisor_;
};
void Print(IDivideResult *div) {
int result = div->result();
int remainder = div->remainder();
std::cout << result << " " << remainder << "\n";
}
int main() {
IDivideResult *div = new LazyDivideResult(10, 3);
Print(div);
}
#包括
#包括
结构IDivideResult{
虚拟int result()=0;
虚拟整数余数()=0;
};
结构DivideResult:IDivideResult{
除法结果(整数结果,整数余数):结果(结果),余数(余数){
int result()重写{返回结果}
int rements()重写{返回余数}
整数结果uu1,余数u2;
};
结构LazyDivideResult:IDivideResult{
LazydividResult(整型被除数,整型除数):被除数,除数{
int result()重写{return Transmogrify()->result();}
int rements()重写{return Transmogrify()->rements();}
DivideResult*Transmogrify(){
int结果=被除数/除数;
整数余数=被除数×除数×除数;
返回新的(此)DivideResult(结果,余数);
}
整型被除数,除数;
};
无效打印(IDivideResult*div){
int result=div->result();
int rements=div->rements();
这是未定义的行为
一旦调用div->result()
,指针div
将无效,因为您已经结束了它所指向的对象的生存期。您观察到的症状是它“成功”调用了余数
理论上,一个实现可以假设您只将DivideResult
传递给Print
,因为传递LazyDivideResult
将是UB。C++编译器不需要使用vtables来实现多态性。事实证明,几乎所有编译器都会这样做。但如果编译器能够看到优化,则可以消除它们,并且仍然导致正确的行为,这是允许的。这可能会有帮助:您是否有兴趣知道如何在没有UB的情况下做到这一点,或者只想确认这一特定代码确实糟糕和错误?我知道如何在没有这种“替换”的情况下实现这一点的方法技巧。我在这里询问编译器的行为。答案似乎是“行为未定义,请参阅编译器文档/代码”。此外,我在std::launder
函数的示例中也看到过此类技巧。因此,可能没有那么多“未定义”?您需要在函数外部清洗指针,以获得所希望的保证。从c++17中清洗std::launder
怎么样?Willstd::launder(div)->rem余数()
定义行为?您还需要将洗钱行为传播回main
,以便安全地删除并且为了进一步避免UB,您最好静态断言(sizeof(DivideResult)它应该是