是否允许成员函数显式调用其 class 析构函数
Is a member function allowed to explicitly call its class destructor
- 以下代码是否具有已定义的行为?
- 如果不是,代码的哪一部分是 UB 以及标准的哪一部分声明它是 UB?
- 如果此代码是 UB,是否有任何可以修复它的[小]更改?
- 如果没有什么可以修复它,还有什么其他代码 scheme/pattern 可以用来实现相同的功能?
class C
{
public:
virtual ~C() {}
virtual void switch_me() = 0;
};
class C1 : public C
{
public:
C1() : b(true) { std::cout << "C1\n"; }
~C1() { std::cout << "~C1\n"; }
private:
void switch_me();
bool b;
};
class C2 : public C
{
public:
C2() : i(1) { std::cout << "C2\n"; }
~C2() { std::cout << "~C2\n"; }
private:
void switch_me();
int i;
};
void C1::switch_me()
{
this->~C1(); // lifetime of *this ends here
std::cout << "blih\n"; // execute some code that does
// not attempt to access object
new(this) C2(); // create a C2 instance in-place
}
void C2::switch_me()
{
this->~C2(); // lifetime of *this ends here
std::cout << "blah\n"; // execute some code...
new(this) C1(); // create a C1 instance in-place
}
class Cnt
{
public:
Cnt() { new(&storage) C1(); }
~Cnt() { (*this)->~C(); }
C* operator->() { return reinterpret_cast<C*>(&storage); }
private:
char storage[std::max(sizeof(C1),sizeof(C2))];
};
int main()
{
Cnt c;
c->switch_me();
c->switch_me();
return 0;
}
您没有关于 switch_me
函数的未定义行为:您在销毁后不以任何方式访问对象,下一次访问发生在新对象上。如果您保存指针和对 C
对象的引用返回 vy operator->
并在根据 3.8/7 调用 switch_me
后使用它,您可能有 UB:
If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:
- the storage for the new object exactly overlays the storage
location which the original object occupied, and
- the new object
is of the same type as the original object (ignoring the top-level
cv-qualifiers), and
- the type of the original object is not
const-qualified, and, if a class type, does not contain any
non-static data member whose type is const-qualified or a reference
type, and
- the original object was a most derived object (1.8) of
type T and the new object is a most derived object of type T (that
is, they are not base class subobjects).
您在其他地方有UB,也就是您的存储。它的对齐方式比您要放置在其中的对象要弱,这可能会导致对齐问题。使用 alignas
关键字指定所需的对齐方式:
alignas(C1) alignas(C2) char storage[std::max(sizeof(C1),sizeof(C2))];
如果两个对齐说明符应用于同一个声明,则使用较大的。
- 以下代码是否具有已定义的行为?
- 如果不是,代码的哪一部分是 UB 以及标准的哪一部分声明它是 UB?
- 如果此代码是 UB,是否有任何可以修复它的[小]更改?
- 如果没有什么可以修复它,还有什么其他代码 scheme/pattern 可以用来实现相同的功能?
class C
{
public:
virtual ~C() {}
virtual void switch_me() = 0;
};
class C1 : public C
{
public:
C1() : b(true) { std::cout << "C1\n"; }
~C1() { std::cout << "~C1\n"; }
private:
void switch_me();
bool b;
};
class C2 : public C
{
public:
C2() : i(1) { std::cout << "C2\n"; }
~C2() { std::cout << "~C2\n"; }
private:
void switch_me();
int i;
};
void C1::switch_me()
{
this->~C1(); // lifetime of *this ends here
std::cout << "blih\n"; // execute some code that does
// not attempt to access object
new(this) C2(); // create a C2 instance in-place
}
void C2::switch_me()
{
this->~C2(); // lifetime of *this ends here
std::cout << "blah\n"; // execute some code...
new(this) C1(); // create a C1 instance in-place
}
class Cnt
{
public:
Cnt() { new(&storage) C1(); }
~Cnt() { (*this)->~C(); }
C* operator->() { return reinterpret_cast<C*>(&storage); }
private:
char storage[std::max(sizeof(C1),sizeof(C2))];
};
int main()
{
Cnt c;
c->switch_me();
c->switch_me();
return 0;
}
您没有关于 switch_me
函数的未定义行为:您在销毁后不以任何方式访问对象,下一次访问发生在新对象上。如果您保存指针和对 C
对象的引用返回 vy operator->
并在根据 3.8/7 调用 switch_me
后使用它,您可能有 UB:
If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:
- the storage for the new object exactly overlays the storage location which the original object occupied, and
- the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and
- the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and
- the original object was a most derived object (1.8) of type T and the new object is a most derived object of type T (that is, they are not base class subobjects).
您在其他地方有UB,也就是您的存储。它的对齐方式比您要放置在其中的对象要弱,这可能会导致对齐问题。使用 alignas
关键字指定所需的对齐方式:
alignas(C1) alignas(C2) char storage[std::max(sizeof(C1),sizeof(C2))];
如果两个对齐说明符应用于同一个声明,则使用较大的。