1. 程式人生 > >03 MFC的動態建立

03 MFC的動態建立

動態建立基本和C++的new雲算符建立沒有區別,但是迴避了C++語言中不讓執行如下物件建立的情形:
char *className = “MyClass”;
CObject *obj = new className;
那MFC動態建立怎麼用呢?這次我們直接思路同上篇一樣,程式碼直接貼巨集展開後的程式碼,上面帶註釋,使用只需將展開程式碼註釋後,將巨集解註釋就是同樣的效果。

/*
 *HelloMFC.h
 */
#ifndef _HELLO_MFC_
#define _HELLO_MFC_

class CMyApp : public CWinApp{
public:
	virtual BOOL InitInstance();
};

class CMainWindow : public CFrameWnd{
	//DECLARE_DYNCREATE(CMainWindow)//此巨集展開為下面的語句
public: 
	static const CRuntimeClass classCMainWindow; 
	virtual CRuntimeClass* GetRuntimeClass() const; 
	static CObject* PASCAL CreateObject();
	
public:
	CMainWindow();
};

#endif

/*
 *HelloMFC.cpp
 */
#include <afxwin.h>
#include "HelloMFC.h"

CMyApp myApp;

BOOL CMyApp::InitInstance()
{
	//m_pMainWnd = (CMainWindow*)RUNTIME_CLASS(CMainWindow)->CreateObject();//展開為下面語句
	m_pMainWnd = (CMainWindow*)((CRuntimeClass*)(&CMainWindow::classCMainWindow))->CreateObject();
	m_pMainWnd->ShowWindow(m_nCmdShow);
	m_pMainWnd->UpdateWindow();

	return TRUE;
}

//IMPLEMENT_DYNCREATE(CMainWindow, CFrameWnd)  //展開為下面語句
CObject* PASCAL CMainWindow::CreateObject()
{ 
	return new CMainWindow;
} 

//IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, class_name::CreateObject, NULL)
const CRuntimeClass CMainWindow::classCMainWindow = { 
	"CMainWindow", sizeof(class CMainWindow), 0xFFFF, CMainWindow::CreateObject,
	((CRuntimeClass*)(&CFrameWnd::classCFrameWnd)), NULL
}; 

CRuntimeClass* CMainWindow::GetRuntimeClass() const
{ 
	return ((CRuntimeClass*)(&CMainWindow::classCMainWindow));
}


CMainWindow::CMainWindow()
{
	Create(NULL, TEXT("Hello MFC"));
}

那麼問題來了,這玩意怎麼實現的,現在我們得回過頭來再看看上篇那個類了!下面貼程式碼加註釋:

struct CRuntimeClass
{
	LPCSTR m_lpszClassName;		//指向類名的指標
	int m_nObjectSize;						//物件大小
	UINT m_wSchema; 						//這個巨集定義填的預設值先不管他
	CObject* (PASCAL* m_pfnCreateObject)(); // 關鍵點來了,函式指標,這就是用來儲存我們上面CreateObject函式地址的指標
#ifdef _AFXDLL									//條件編譯,動態連結庫時使用
	CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
#else
	CRuntimeClass* m_pBaseClass;		//我們選擇的靜態連結庫使用這個來指向父類
#endif

	CObject* CreateObject();
	BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;

	// dynamic name lookup and creation
	static CRuntimeClass* PASCAL FromName(LPCSTR lpszClassName);
	static CRuntimeClass* PASCAL FromName(LPCWSTR lpszClassName);
	static CObject* PASCAL CreateObject(LPCSTR lpszClassName);
	static CObject* PASCAL CreateObject(LPCWSTR lpszClassName);

    // 先不去管
	void Store(CArchive& ar) const;
	static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);

	// 用單鏈表來連線執行時類物件
	CRuntimeClass* m_pNextClass;       // 連線已經註冊的類
	const AFX_CLASSINIT* m_pClassInit;//預設NULL,先不管吧
};

接下來我們在上面貼的示例程式碼此處下斷點:
在這裡插入圖片描述
然後F11跟進去來到下面程式碼出,下面貼出程式碼註釋寫一下:

CObject* CRuntimeClass::CreateObject()
{
	ENSURE(this);//用來丟擲異常的,感興趣可以跟進去看下

	if (m_pfnCreateObject == NULL)//函式指標判斷
	{
		TRACE(traceAppMsg, 0,
			_T("Error: Trying to create object which is not ")
			_T("DECLARE_DYNCREATE \nor DECLARE_SERIAL: %hs.\n"),
			m_lpszClassName);
		return NULL;
	}

	CObject* pObject = NULL;
	TRY				//自行跟一下咯,異常處理
	{
		pObject = (*m_pfnCreateObject)();//呼叫了我們的CreateObject函式
	}
	END_TRY

	return pObject;
}

說了那麼多,我們通過名字來建立物件的問題還沒有解決啊!這時,我們應該想想上篇我們說執行程式前跟我們建立了一個繼承連結串列,這點向同了,那麼我們就來貼下面程式碼咯!

/*
 *HelloMFC.h
 */
#ifndef _HELLO_MFC_
#define _HELLO_MFC_
#include <iostream>

class CMyApp : public CWinApp{
public:
	virtual BOOL InitInstance();
};

class CMainWindow : public CFrameWnd{
	//DECLARE_DYNCREATE(CMainWindow)//此巨集展開為下面的語句
public: 
	static const CRuntimeClass classCMainWindow; 
	virtual CRuntimeClass* GetRuntimeClass() const; 
	static CObject* PASCAL CreateObject();
	
	//通過名字來建立物件
	static CObject* CreateObjectByName(const char *pClassName);
	static CRuntimeClass* FromName(const char *pClassName);
public:
	CMainWindow();
};

#endif

/*
 *HelloMFC.h
 */
#ifndef _HELLO_MFC_
#define _HELLO_MFC_
#include <iostream>

class CMyApp : public CWinApp{
public:
	virtual BOOL InitInstance();
};

class CMainWindow : public CFrameWnd{
	//DECLARE_DYNCREATE(CMainWindow)//此巨集展開為下面的語句
public: 
	static const CRuntimeClass classCMainWindow; 
	virtual CRuntimeClass* GetRuntimeClass() const; 
	static CObject* PASCAL CreateObject();
	
	//通過名字來建立物件
	static CObject* CreateObjectByName(const char *pClassName);
	static CRuntimeClass* FromName(const char *pClassName);
public:
	CMainWindow();
};

#endif

/*
 *HelloMFC.cpp
 */
#include <afxwin.h>
#include "HelloMFC.h"

CMyApp myApp;

BOOL CMyApp::InitInstance()
{
	m_pMainWnd = (CMainWindow*)CMainWindow::CreateObjectByName("CMainWindow");//通過類名來建立物件
	m_pMainWnd->ShowWindow(m_nCmdShow);
	m_pMainWnd->UpdateWindow();

	return TRUE;
}

//IMPLEMENT_DYNCREATE(CMainWindow, CFrameWnd)  //展開為下面語句
CObject* PASCAL CMainWindow::CreateObject()
{ 
	return new CMainWindow;
} 

//IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, class_name::CreateObject, NULL)
const CRuntimeClass CMainWindow::classCMainWindow = { 
	"CMainWindow", sizeof(class CMainWindow), 0xFFFF, CMainWindow::CreateObject,
	((CRuntimeClass*)(&CFrameWnd::classCFrameWnd)), NULL
}; 

CRuntimeClass* CMainWindow::GetRuntimeClass() const
{ 
	return ((CRuntimeClass*)(&CMainWindow::classCMainWindow));
}


CMainWindow::CMainWindow()
{
	Create(NULL, TEXT("Hello MFC"));
}

CObject* CMainWindow::CreateObjectByName(const char *pClassName)
{
	if (pClassName == nullptr){
		return nullptr;
	}
	CRuntimeClass* pClass = FromName(pClassName);
	if (pClass == nullptr){
		return nullptr;
	}
	CObject* pObject = pClass->CreateObject();
	return pObject;
}

CRuntimeClass* CMainWindow::FromName(const char *pClassName)
{
	if (pClassName == nullptr){
		return nullptr;
	}
	CRuntimeClass* pClass = NULL;
	for (pClass = (CRuntimeClass*)(&CMainWindow::classCMainWindow); pClass != NULL; pClass = pClass->m_pBaseClass){
		if (lstrcmpA(pClassName, pClass->m_lpszClassName) == 0){
			return pClass;
		}
	}
	return NULL; 
}