实验: 虚函数细节验证.

在这之前,对类析构函数为虚函数的理解是: 如果不为虚函数, 删除子类时,由基类开辟的资源无法释放.

今天调试了一个BUG, 是使用vector时, 因为基类析构函数不是虚函数,引起程序崩溃. 因为涉及到第三方Dll, 找来找去的费了2天.

 

// vectorTest.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <vector> #include "vectorTest.h" #include "ClassTest.h" #include "ClassTestSub1.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // The one and only application object std::vector<CClassTest*> listPersistentNodes1; CWinApp theApp; using namespace std; int fnVectorTest1(); int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; // initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs cerr << _T("Fatal Error: MFC initialization failed") << endl; nRetCode = 1; } else { // TODO: code your application's behavior here. CString strHello; strHello.LoadString(IDS_HELLO); cout << (LPCTSTR)strHello << endl; fnVectorTest1(); } printf("END, press any key to quit/n"); getchar(); return nRetCode; } int fnVectorTest1() { int n = 0; int nMax = 2; CClassTest * pCur = NULL; listPersistentNodes1.clear(); TRACE("/n填充vector/n"); for(n = 0; n < nMax; n++) { if(0 == (n % 2)) { pCur = new CClassTest(); pCur->SetType("基类"); TRACE("pCur[%p] = new CClassTest();/n", pCur); } else { pCur = new CClassTestSub1(); pCur->SetType("子类"); TRACE("pCur[%p] = new CClassTestSub1();/n", pCur); } listPersistentNodes1.push_back(pCur); } TRACE("/n显示vector/n"); nMax = listPersistentNodes1.size(); for(n = 0; n < nMax; n++) { pCur = listPersistentNodes1[n]; TRACE("pCur[%p] = listPersistentNodes[%d];/n", pCur, n); } TRACE("/n删除vector/n"); nMax = listPersistentNodes1.size(); for(n = 0; n < nMax; n++) { pCur = listPersistentNodes1[n]; TRACE("pCur[%p] = listPersistentNodes1[%d];/n", pCur, n); delete pCur; } /** * @note * 构造的时候, 先进基类的构造函数, 再进子类的构造函数, * 不用在子类的构造函数中手工执行基类的构造函数 * * 析构函数为虚函数时, * 析构时, 先进子类的析构函数, 再进基类的构造函数 */ /** * @note 运行结果 正确 填充vector [00382910]CClassTest::CClassTest() pCur[00382910] = new CClassTest(); //!!! 正确的情况: 基类指针和子类指针相同 [003829F8]CClassTest::CClassTest() [003829F8]CClassTestSub1::CClassTestSub1() pCur[003829F8] = new CClassTestSub1(); 显示vector pCur[00382910] = listPersistentNodes[0]; pCur[003829F8] = listPersistentNodes[1]; 删除vector pCur[00382910] = listPersistentNodes1[0]; [00382910]CClassTest::~CClassTest() pCur[003829F8] = listPersistentNodes1[1]; [003829F8]CClassTestSub1::~CClassTestSub1() [003829F8]CClassTest::~CClassTest() nMax[0] = listPersistentNodes1.size(); */ /** 当基类析构函数不为空时, 导致程序崩溃 从TRACE出来的运行结果看, 能很清除的看到, 从vector取出来的类指针,是基类的指针 * @note 错误的运行结果 填充vector [00382910]CClassTest::CClassTest() pCur[00382910] = new CClassTest(); //!!! 错误的情况: 基类指针和子类指针不相同 //!!! 原因: 基类的析构函数不是虚函数 [003829FC]CClassTest::CClassTest() [003829F8]CClassTestSub1::CClassTestSub1() pCur[003829FC] = new CClassTestSub1(); 显示vector pCur[00382910] = listPersistentNodes[0]; pCur[003829FC] = listPersistentNodes[1]; 删除vector pCur[00382910] = listPersistentNodes1[0]; [00382910]CClassTest::~CClassTest() pCur[003829FC] = listPersistentNodes1[1]; [003829FC]CClassTest::~CClassTest() HEAP[vectorTest.exe]: Invalid Address specified to RtlValidateHeap( 00380000, 003829DC ) */ listPersistentNodes1.clear(); nMax = listPersistentNodes1.size(); TRACE("nMax[%d] = listPersistentNodes1.size();/n", nMax); return S_OK; } /** * @note * 总结: 这个BUG是在提炼XMLEditor(Mukit, Ataul)中的XML操作是发现的问题 * 原版应用是打开一个xml文件, 显示出内容, 提供增删改查的功能, 根本没有我遇到的这个BUG * * 我的应用是封装自定义XML类, 针对Buffer进行操作, 构造解析XMLBuffer * class CLsXMLParserXerces : public TXMLParserXerces 只提供业务层使用的接口 class CLsXmlOperationLayer { public: CLsXmlOperationLayer(); virtual ~CLsXmlOperationLayer(); //针对具体应用的的XML构造分析 SCODE FormXml_Function_x(PTCHAR &pcXmlBuf, UINT &uLen);//构造xml_用途1, 构造其他用途的xml参照这个函数 SCODE WirteFile(PTCHAR pcFileName, PBYTE pBuf, UINT uLen); private: //调试用的函数, 程序调通后, 注释掉函数内的实现 SCODE TestPoint(); private: CLsXMLParserXerces * m_Parser; }; * * * 自己构造了这个环境相同的Demo, 才看出来. 从崩溃信息看不到错在哪. * 一步步的追下去, 才看到是基类指针和子类的指针地址不相同 * * 看来类的析构函数一定要定义成虚函数, 这是个好习惯 * * 反思Mukit, Ataul没有把基类的析构函数定义成虚函数的原因 * 1. 他的应用中没有报错~ * 2. 那个类比较小型, 刚好100行. * 为了简练, 他把实现也全写在一个.h中. 这样他自己就没注意到 * 我认为他没注意到的原因是他的应用没有报错... */

// ClassTest.h: interface for the CClassTest class. // ////////////////////////////////////////////////////////////////////// #if !defined(AFX_CLASSTEST_H__49EAB2BE_E4F6_4F96_8ED8_BA2D48325556__INCLUDED_) #define AFX_CLASSTEST_H__49EAB2BE_E4F6_4F96_8ED8_BA2D48325556__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 class CClassTest { public: CClassTest(); //!!!基类析构函数必须为虚函数, 否则使用vector时, 删除压入的子类指针时报错 virtual ~CClassTest(); //~CClassTest(); public: void SetID(INT nID) {m_nID = nID;} INT GetID() {return m_nID;} void SetType(LPCSTR pcType) {m_strType = pcType;} CString GetType() {return m_strType;} private: INT m_nID; CString m_strType; }; #endif // !defined(AFX_CLASSTEST_H__49EAB2BE_E4F6_4F96_8ED8_BA2D48325556__INCLUDED_)

// ClassTest.cpp: implementation of the CClassTest class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "vectorTest.h" #include "ClassTest.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CClassTest::CClassTest() { CString strMsg; strMsg.Format("[%p]CClassTest::CClassTest()/n", this); OutputDebugString(strMsg); m_nID = -1; } CClassTest::~CClassTest() { CString strMsg; strMsg.Format("[%p]CClassTest::~CClassTest()/n", this); OutputDebugString(strMsg); }

// ClassTestSub1.h: interface for the CClassTestSub1 class. // ////////////////////////////////////////////////////////////////////// #if !defined(AFX_CLASSTESTSUB1_H__DA789096_2EFB_4BD7_A806_D951957A52E4__INCLUDED_) #define AFX_CLASSTESTSUB1_H__DA789096_2EFB_4BD7_A806_D951957A52E4__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include "ClassTest.h" class CClassTestSub1 : public CClassTest { public: CClassTestSub1(); virtual ~CClassTestSub1(); }; #endif // !defined(AFX_CLASSTESTSUB1_H__DA789096_2EFB_4BD7_A806_D951957A52E4__INCLUDED_)

// ClassTestSub1.cpp: implementation of the CClassTestSub1 class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "vectorTest.h" #include "ClassTestSub1.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CClassTestSub1::CClassTestSub1() { CString strMsg; strMsg.Format("[%p]CClassTestSub1::CClassTestSub1()/n", this); OutputDebugString(strMsg); } CClassTestSub1::~CClassTestSub1() { CString strMsg; strMsg.Format("[%p]CClassTestSub1::~CClassTestSub1()/n", this); OutputDebugString(strMsg); } 

<2011_0207_1140>

重构数据类时发现,基类调用子类的纯虚函数, 继承自cobject能运行, 但是结果不对. 如果基类没有继承任何类, 编译不过.

查了资料, 看到构造函数和析构函数不能调用虚函数, 原因是子类执行时,不能调到子类的虚函数.

<<Never Call Virtual Functions during Construction or Destruction>>
http://www.artima.com/cppsource/nevercall.html


我在构造函数和析构函数中,确实调用了虚函数.

做实验, 解决在基类中调用子类的纯虚函数的问题. 多态可以这样的, 记得实际工程中有这样的用法.

 

<2011_0208_0359>

修改了新的数据类架子, 验证了虚函数引起的问题.

实验结论:

* 确实不能在构造函数, 析构函数, 拷贝构造函数中直接或间接的调用虚函数或纯虚函数.

* 可以在构造,析构,拷贝构造之外的函数调用虚函数, 可以在基类中调用子类中实现的纯虚函数.

* 每个类必须实现自己的拷贝构造函数.

数据类架子工程更新: http://blog.csdn.net/LostSpeed/archive/2010/11/03/5984846.aspx

 

你可能感兴趣的:(xml,vector,File,application,initialization,construction)