Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/142.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 我可以删除联合组件而不检查它是否存储该对象吗?_C++_C++11 - Fatal编程技术网

C++ 我可以删除联合组件而不检查它是否存储该对象吗?

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 :

我的意思如下。我有几个类继承了相同的基类。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 : 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
的指针
删除
a
B
。§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
    派生的。是的,我存储了关于使用了什么选择组件的信息。只是想消除销毁过程中的切换。检查我添加的脚注,如果您不习惯使用类型转换,这可能会有所帮助-