一个例子
这是一个精心设计,极度简化的例子,只为说明问题,并不是产品代码。
A.h的内容
#include <memory> // for auto_ptr
class B; // forward decleration
class A
{
public:
A();
private:
std::auto_ptr<B> m_oB;
};
A.cpp的内容
#include “A.h”
class B
{
};
A::A()
{
}
Main.cpp的内容
#include “A.h”
int main()
{
A a;
return 0;
}
用VC7.1编译Main.cpp,我们会得到以下警告:
warning C4150: deletion of pointer to incomplete type 'B'; no destructor called.
while compiling class template member function 'std::auto_ptr<_Ty>::~auto_ptr(void)'
with
[
_Ty=B
]
原因
稍有经验的人一眼就能看出,之所以会有这个警告是因为在编译Main.cpp时,编译器只看见了B的前置声明,
而不知道B的定义。但在a析构时,需要用到B的定义,即B的析构函数。
解决方案
天真方案
最天真最简单的方法就是将B的定义从A.cpp中移到A.h中,Main.cpp就拥有了B的定义。编译警告也就自然消失了。
人常说简单就是美,但是不适合这里。将B的定义放在A.h中,使得所有依赖A的地方,也都依赖B。Main.cpp只依赖A,现在也依赖B了。任何对B定义的修改都会引起Main.cpp重新编译。这种编译依赖本不应该存在。对于拥有成千上万源文件的项目来说,这种依赖会导致编译时间增长,以致到不能接受的地步。
可行方案
作为一个有责任感的程序员,应该要寻求一个更可行的方案。
首先,我们来深入挖掘一下警告的原因。没错,Main.cpp中没有B的定义。但为什么需要B的定义呢?因为a在析构时,其成员m_oB会析构。m_oB会delete 其拥有的B指针,从而引起B的析构函数的调用。没有B的定义,编译器没法做这件事,所以只能抱怨:喂,这个类不完整,没有析构函数可调用。
但但但是为什么需要B的定义呢?m_oB析构应该是a析构函数的内部实现,Main.cpp应该只需要知道a的析构函数接口就行了。呀,A没有定义析构函数。按照C++标准,如果一个类没有显式声明析构函数,编译器必须为其隐式生成一个析构函数且这个隐式析构函数必须是内联和公有的。内联?哈哈,就是你了。原来A的析构实现被include进了Main.cpp,A的析构实现需要B的析构,而Main.cpp没有B的析构函数定义。
原因清楚了,解决方案就呈现出来了。在A.h中为A声明一个析构函数(可别内联哦)。在A.cpp中为A的析构函数定义一个空的函数体。修改过的文件内容如下,其中红色部分是新加内容。
A.h的内容
#include <memory> // for auto_ptr
class B; // forward decleration
class A
{
public:
A();
~A();
private:
std::auto_ptr<B> m_oB;
};
A.cpp的内容
#include “A.h”
class B
{
};
A::A()
{
}
A::~A()
{
}
Main.cpp的内容
#include “A.h”
int main()
{
A a;
return 0;
}
额外感触
原来内联有时真的会出问题。