C++ 我可以删除联合组件而不检查它是否存储该对象吗?
我的意思如下。我有几个类继承了相同的基类。Union由以下类的指针组成:C++ 我可以删除联合组件而不检查它是否存储该对象吗?,c++,c++11,C++,C++11,我的意思如下。我有几个类继承了相同的基类。Union由以下类的指针组成: #include "stdio.h" class A { public: A() { printf("A\n"); } virtual ~A() { printf("~A\n"); } }; class B : public A { public: B() { printf("B\n"); } virtual ~B() { printf("~B\n"); } }; class C :
#include "stdio.h"
class A {
public:
A() { printf("A\n"); }
virtual ~A() { printf("~A\n"); }
};
class B : public A {
public:
B() { printf("B\n"); }
virtual ~B() { printf("~B\n"); }
};
class C : public A {
public:
C() { printf("C\n"); }
virtual ~C() { printf("~C\n"); }
};
int main() {
union {
B* b;
C* c;
} choice;
choice.b = new B();
delete choice.c; //We have B object, but deleting C
return 0;
}
它似乎有效,但我不确定它是否是特定于实现的行为。我可以使用这种奇怪的删除方法吗?或者我应该记住一种类型的存储对象并分别删除它吗
我使用C++ 11,希望它在GCC和Visual C++(2012和更高)上都能工作。在实际的项目中,我有更复杂的类层次结构,但它们都是同一抽象基类的继承者(直接或间接)。您只允许读取上次写入的联合成员,并且只允许通过指向基类的指针删除对象(如果对象具有虚拟析构函数)。它现在似乎可以工作,但您可能会发现它在将来会随机中断,通常是由于一个积极的优化器
为什么不存储一个指向
a
的指针,而不是union?这是未定义行为的两倍。首先,您不能通过指向C
的指针删除aB
。§5.3.5[解释删除]/p3:
在第一个备选方案(删除对象)中,如果
要删除的对象与其动态类型(静态)不同
类型应为待处理对象的动态类型的基类
已删除,静态类型应具有虚拟析构函数或
行为是未定义的。在第二个备选方案(删除数组)中,如果
要删除的对象的动态类型与其静态类型不同,
该行为未定义
第二,也是C++中未定义的行为。
无论如何,这里没有必要使用工会<代码> B<代码>代码> C >代码>共享同一基类,因此您可以将指针存储在<代码> A*<代码> > ,正如在其他答案中所说的,这不是正确的C++。
我的印象是,您希望保持指针的并集,因为在某些情况下,您需要一个
B
的(子)类实例,而在另一个C
实例中,B
和C
的接口并不完全相同。也许您将其中几个存储在一个容器中,或者只是在运行时之前不知道将使用哪个实例
因此,您可以保持代码原样,可能在某个地方有一个类型标记指示创建了哪个实例,然后在每次需要确定要运行的正确代码时使用开关,或者您可以利用类在运行时实际调用适当的函数,通过在B
和C
(1)的公共基类中包含一个虚拟方法,并使用开关的正确分支在B
和C
中重载此方法,然后用指向基类的简单指针替换联合
(1)基类不一定是<代码> A<代码>:如果不想杂类类,只需在其上生成一个具有最小接口的不同类,并且由于C++多重继承,有<代码> B<代码>和<代码> C <代码>。别忘了虚拟析构函数
对我来说,这个案子看起来合法,没有未定义的行为
他使用类型双关,这是合法的,因为它在联合成员中的B*B和C*C是指针,可以转换为字符数组
B和C都有虚拟析构函数,因为基类(不是因为基类相同!而是因为基类有虚拟析构函数)
12.4.9如果一个类有一个带有虚拟析构函数的基类,那么它的析构函数(无论是用户还是隐式声明的)都是虚拟的
当析构函数调用时,(因为它是虚拟的),将从选择变量中提取准确的函数地址,并调用正确的析构函数序列。因此,根本不存在任何未定义的行为
明显的未定义行为是明显的。联合的成员占用相同的字节。您正在分配成员b
,该成员将覆盖成员c
,然后使用指向b
的指针调用c::~c
。您想要做的是合理的,但您必须有某种方法知道您在那里拥有什么类型的对象。另外,保存额外指针的大小是否如此重要?为什么不能有两个指针呢?B和C的vtables对齐,这可能是幸运的。对于任何非琐碎(甚至琐碎)的情况,您都不能指望这一点。为什么不只是按你的需要来做一个*object =新的b-(或)新的()(VisualC++ 2012)打印如下:<代码> A/>代码> B> <代码>代码> ~b>代码>代码> >代码>没有C语言,C和C包含其他方法和字段。因此,由于更高的性能,我应该使用具有不同指针的并集。我通过特殊的方法访问这个联合,这些方法检查存在性并返回null ptr或需要类型的有效指针。我认为使用动态_cast将减少performance@user2717575,你是什么意思?在当前的解决方案中,您必须已经有某种机制来确定对象的动态类型,以便访问联合的正确成员。使用A*
,您使用相同的机制,然后只需static\u cast
。是的,您完全正确,static\u cast可以工作,但据我所知,从A*到B*进行static\u cast并不安全。这就是为什么我不敢使用从A*到B*的静态强制转换,即使我知道它是正确的。@user2717575,那么你现在可以放心了:你可以安全地从A*
到B*
进行静态强制转换,只要对象的动态类型是从B
派生的。是的,我存储了关于使用了什么选择组件的信息。只是想消除销毁过程中的切换。检查我添加的脚注,如果您不习惯使用类型转换,这可能会有所帮助-