最近又遇到一个奇葩问题,程序在自己的开发机器和某些机器上运行完好,但是在测试人员的几台机器上运行就直接推出了。开始以为是出现了野指针,因为delete野指针时程序会直接退出。代码翻来覆去过来即便确认没有野指针后问题就陷入了死循环。经过多次调试我发现在我的机器上虽然不崩溃,但是delete对象指针的时候不会走对应的析构函数,这问题就奇怪了。后来终于被我找到了原因。原来在头文件中声明成员变量指针时为了尽量少的包含头文件而使用的前向声明,而在实现文件中又没有包含真正声明该类型的头文件。一般情况下这种使用方式编译器会报错,但是当把指针放在容器中时编译器就不能侦测到错误了。说起来问题比较绕下面用代码写了个例子:
father.h
#ifndef FATHER_H_ #define FATHER_H_ struct FatherCalss { public: virtual double Speak() = 0; virtual ~FatherCalss(); }; struct SonCalss:public FatherCalss { public: virtual ~SonCalss(); }; class GrandsonClass:public SonCalss { public: virtual double Speak(); virtual ~GrandsonClass(); }; #endif//FATHER_H_
father.cpp
#include "stdafx.h" #include "Father.h" #include <Windows.h> FatherCalss::~FatherCalss() { MessageBox(NULL, _T("FatherCalss"), _T("~FatherCalss"), MB_OK); } SonCalss::~SonCalss() { MessageBox(NULL, _T("SonCalss"), _T("~SonCalss"), MB_OK); } GrandsonClass::~GrandsonClass() { MessageBox(NULL, _T("GrandsonClass"), _T("~GrandsonClass"), MB_OK); } double GrandsonClass::Speak() { MessageBox(NULL, _T("Speak"), _T("~GrandsonClass"), MB_OK); return 0.0; }
first.h
#ifndef FIRST_H__ #define FIRST_H__ #include <vector> struct FatherCalss; class First { public: void CreateObject(); ~First(); std::vector<FatherCalss*> m_VecpFather; }; #endif // First_h__
first.cpp
#include "First.h" #include "Father.h" void First::CreateObject() { FatherCalss* pFth = new GrandsonClass; pFth->Speak(); m_VecpFather.push_back(new GrandsonClass); } First::~First() { for(std::vector<FatherCalss*>::iterator it = m_VecpFather.begin(); it != m_VecpFather.end(); ++it) { delete *it; } }
调用代码:
First* pFirst = new First; pFirst->CreateObject(); for(std::vector<FatherCalss*>::iterator it= pFirst->m_VecpFather.begin(); it != pFirst->m_VecpFather.end(); ++it) { delete *it; } pFirst->m_VecpFather.clear(); delete pFirst;
如上如果 m_VecpFather 不是容器而是 FatherCalss* 则编译器会报错。
运行程序后会发现对象的析构函数根本就没有被执行,这样的行为存在一定的不确定性,程序如果不报错的换实际上会产生内存泄漏。