当我在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因为 它创建了以下成员 IDisisable类型:'A'。
要解决此警告,我必须将此代码添加到B类:
~B() { delete m_a; }
但我不明白为什么。 A类通过其析构函数(和终结器)实现IDisposable 所以当A被垃圾收集时,A的终结器或析构函数将被调用,从而释放其非托管资源。
为什么B必须在其A成员上添加析构函数来调用'delete'? 如果B显式调用“delete m_a”,那么GC是否只调用A的析构函数?
编辑:如果您使用声明A成员的“syntax sugar”方法,它似乎会自动生效,如下所示:
ref class B
{
public:
B() { }
protected:
A m_a;
};
但这并非总是可行。
为什么GC没有足够聪明地自动处理A ^的托管引用指针,一旦没有其他人有指向它的指针?
答案 0 :(得分:2)
您应该为成员使用堆栈语义,并将析构函数添加到包含的类中。 然后该成员将被处置。 见http://msdn.microsoft.com/en-us/library/ms177197.aspx
ref class B
{
public:
B() {}
~B() {}
protected:
A m_a;
};
该成员仍然是裁判。输入并仍然在堆上创建。
编辑:
在.net中处理充其量是不幸的,在C#中,整个确定性行为都被打破了,你必须对Dispose调用非常危险,以获得大多数c ++开发人员所期望的行为。
在c ++ / cli中,堆栈语义使它更好。如果你不能使用它们,你就必须明确地调用dispose,它在c ++ / cli中由析构函数表示。
自动链接调用成员的唯一方法是通过堆栈语义,如果成员是普通的托管指针,就像c#一样,你必须手动链接调用。
许多类可以保存相同的A ^指针,无法知道哪一个应该调用析构函数。
您收到警告,因为您已经实现了析构函数,这会导致您的类实现IDispose。这使您有机会以确定的方式进行清理。
GC单独只能收集没有引用的对象并调用终结器。这远非确定性的。请注意,依靠终结器进行清理应该是一个安全网,因为它可能会在将来被称为很长时间。
我建议您尝试设计代码以允许上述模式。