3

当我在 VS2008 中构建以下 C++/CLI 代码时,会显示代码分析警告 CA1001。

ref class A
{
public:
    A()   { m_hwnd = new HWND; }
    ~A()  { this->!A(); }
protected:
    !A()  { delete m_hwnd; }
    HWND* m_hwnd;
};

ref class B
{
public:
    B()   { m_a = gcnew A(); }
protected:
    A^    m_a;
};

警告:CA1001:Microsoft.Design:在“B”上实现 IDisposable,因为它创建了以下 IDisposable 类型的成员:“A”。

要解决此警告,我必须将此代码添加到 B 类:

    ~B()  { delete m_a; }

但我不明白为什么。A 类通过其析构函数(和终结器)实现 IDisposable。
因此,当 A 被垃圾回收时,肯定会调用 A 的终结器或析构函数,从而释放其非托管资源。

为什么 B 必须添加一个析构函数才能在其 A 成员上调用“删除”?
如果 B 显式调用“删除 m_a”,GC 是否只会调用 A 的析构函数?


编辑:如果您使用声明 A 成员的“语法糖”方法,这似乎会自动工作,如下所示:

ref class B
{
public:
    B()   { }
protected:
    A     m_a;
};

但这并不总是可能的。

一旦没有其他人拥有指向它的指针,为什么 GC 不够聪明地自动处理 A^ 的托管引用指针?

4

1 回答 1

2

您应该对成员使用堆栈语义并向包含的类添加析构函数。然后该成员将被处置。请参阅http://msdn.microsoft.com/en-us/library/ms177197.aspx

ref class B
{
public:
    B()   {}
    ~B()  {}
protected:
    A    m_a;
};

该成员仍然是裁判。类型并且仍然在堆上创建。

编辑:

.net 中的 Dispose 充其量是不幸的,在 C# 中,整个确定性行为被破坏了,您必须非常严格地处理 Dispose 调用才能获得大多数 c++ 开发人员期望的行为。

在 c++/cli 中,堆栈语义使它变得更好。如果您不能使用它们,您将不得不显式调用在 c++/cli 中由析构函数表示的 dispose。

如果成员是像 c# 一样的普通托管指针,则自动链接对成员的 dispose 调用的唯一方法是通过堆栈语义,您必须手动链接调用。

许多类可以持有相同的 A^ 指针,没有办法知道哪个应该调用析构函数。

您收到警告是因为您已经实现了导致您的类实现 IDispose 的析构函数。这使您有机会以确定的方式进行清理。

单独的 GC 只能收集没有引用的对象并调用终结器。这远非确定性。请注意,依靠终结器进行清理应该只是一个安全网,因为它可能会在未来很长一段时间内被调用。

我建议尝试设计您的代码以允许上述模式。

于 2009-08-12T12:59:34.633 回答