0

抱歉,如果已经问过这个问题,但我很难搜索析构函数和访问冲突 =)

这是场景的 C++ 伪代码:


在 DLL1 中(使用 /MT 编译)

class A
{
public:
    virtual ~A()      <== if "virtual" is removed, everthing works OK
    {
    }
}

class B : public A
{
public:
    __declspec( dllexport ) ~B()  // i did try without exporting the destructor as well
     {
     }      <== Access Violation as it returns (if fails in assembly at delete operator...)
}

在链接到 DLL1 的 DLL2 中

main()enter code here
{
    B* b = new B();
    delete b;           <== Access Violation
}

到底是怎么回事?我是不是脑子有问题?如果我将 A 的析构函数设为非虚拟的,那么一切正常——甚至 A 和 B 的析构函数也被调用(好像 A 的析构函数是虚拟的——这是因为它是公共的吗?)。

不过,我的主要问题是 - 为什么当基类的析构函数被声明为虚拟时会出现访问冲突?

4

2 回答 2

1

多谢你们!谢谢 ChrisW .. 看起来这正是正在发生的事情。我只需要添加一个静态分配器(静态 createNew()):

class A
{
public:
  __declspec( dllexport ) static void destroy(A* self) { delete self; }
protected:
  virtual ~A() {}
};

class B : public A
{
 protected: 
   B();
 public:
   __declspec( dllexport ) static B* createNew() { return new B(); }
}

int main()
{
  B* b = B::createNew()
  A::destroy(b); //instead of delete b
  return 0;
}

(顺便说一句,考虑到我的部署环境,使用 /MD 编译不是我的选择)

于 2009-12-03T21:14:05.017 回答
0

因为它在 delete 运算符中崩溃,并且因为你说你正在编译/MT,所以我相信原因是你的两个 DLL 不共享同一个堆:因为它们每个都链接到一个静态库,它们是每个人都有自己的运行时堆的私有副本;并且您有效地从一个堆中分配一个 DLL 中的内存,并从另一个堆中删除另一个 DLL 中的内存。

要解决此问题,您可以声明析构函数受保护,并且不要导出它。相反,创建一个静态销毁函数:

class A
{
public:
  __declspec( dllexport ) static void destroy(A* self) { delete self; }
protected:
  virtual ~A() {}
};

int main()
{
  B* b = new B();
  A::destroy(b); //instead of delete b
  return 0;
}

或者,您可能更喜欢这个,因为它不涉及更改源代码,确保两个 DLL 都构建为使用相同的堆,即使用 C 运行时的 DLL 版本,我认为这意味着使用/MD选项而不是/MT.

于 2009-12-03T04:14:47.697 回答