指针指针崩溃时指针指针崩溃

时间:2010-12-17 20:23:41

标签: c++ windows

我正在将一些代码移植到Windows并且难以接受。有一些代码在启动时自动运行以将指针复制到指针,并在退出时再次运行以删除指向指针的指针(如果它不为空。)

我创建了一个示例程序来重现行为

int main()
{
  // Pointer to a Pointer, current crash.
  InterfaceClass** ptrptr;
  ConcreteTwo* object = new ConcreteTwo();
  ptrptr = (InterfaceClass**)(&object); // cast is required for some reason.
  delete *ptrptr; // Crash here.

  // Single pointer, works fine.
  InterfaceClass* ptrptr;
  ConcreteTwo* object = new ConcreteTwo();
  ptrptr = object;
  delete ptrptr;

  // There are other cases where there are only 3 classes in the hierarchy.
  // This also works fine.
  InterfaceClass** ptrptr;
  ConcreteOne* object = new ConcreteOne();
  ptrptr = (InterfaceClass**)(&object);
  delete *ptrptr;

  return 0;
}

类层次结构如下所示。基类是一个带有一些纯虚函数的接口,并且被整个程序中的许多类包含,使得许多对象可能从多个地方继承它。因此,具体实现必须使用“public virtual InterfaceClass”扩展它。在此示例中,删除“虚拟”可解决崩溃。

class InterfaceClass {
public:
    virtual ~InterfaceClass() {};
    InterfaceClass() {}
};

class ConcreteClass : public virtual InterfaceClass {
public:
  ConcreteClass() { }
  virtual ~ConcreteClass() {}
};

class ConcreteOne : public ConcreteClass
{
public:
  ConcreteOne(void) {}
  virtual ~ConcreteOne(void) {}
};

class ConcreteTwo : public ConcreteOne
{
public:
  ConcreteTwo(void) {}
  virtual ~ConcreteTwo(void) {}
};

4 个答案:

答案 0 :(得分:5)

那么你是否熟悉指针的类型与它指向的类型几乎没有任何关系?换句话说,如果你认为T1从T2继承T1 *也继承自T2 *?那是错的。现在,这对您目前的情况有何影响?

  InterfaceClass** ptrptr;
  ConcreteTwo* object = new ConcreteTwo();
  ptrptr = (InterfaceClass**)(&object); // cast is required for some reason.

这是C风格转换的主要问题。好的,所以它节省了一些水平空间,但你甚至知道你刚刚做了什么样的演员表?这不是你的想法。您实际上从ConcreteTwo *类型执行了 reintpret_cast 到一个不相关的类型InterfaceClass *!现在指针地址与你说的类型无关。

然后你将一个重新解释的指针类型抛入删除状态,这会立即导致你违反自己的括约肌。

答案 1 :(得分:3)

好吧,编译器警告过你,你决定按照自己的方式去做......

你不能做这个演员:

ptrptr = (InterfaceClass**)(&object);

因为object指向ConcreteTwo,这与InterfaceClass不同。 InterfaceClass的{​​{1}}子对象位于不同的地址。 ConcreteTwo不是指向*ptrptr实例的指针。

传递给InterfaceClass的指针是指向delete的指针,但是你向编译器说它是指向ConcreteTwo的指针。 InterfaceClass假设它确实是delete,因此崩溃。

答案 2 :(得分:2)

答案 3 :(得分:1)

我认为问题出现在铸造线上。顺便说一句,如果您删除插入的强制转换,编译器会准确地告诉您这是什么问题。

如果你真的想这样做,我强烈建议你这样做,你应该首先创建一个临时的:

ConcreteTwo* object = new ConcreteTwo();
InterfaceClass* ptr = object;

然后您可以获取其地址并将其分配给ptrptr变量:

InterfaceClass** ptrptr = &ptr;

现在你可以安全地删除它:

delete *ptrptr;

考虑到如果ptr超出ptrptr之前的范围,则删除可能会再次崩溃。

至于其余部分,Noah解释了为什么你的代码无效。