所謂Cross_Dll問題,就是在一個dll中申請了一段記憶體空間,在外部程式調用完該dll提供的功能後,為了不造成記憶體泄露,要釋放掉在dll內部申請的空間。但是這種操作會引起程式崩潰。如下面的樣本程式所示:
#ifndef DLL_NEW_H_#define DLL_NEW_H_#ifndef _DLL_NEW_DLL_#define DLL_NEW_API __declspec(dllimport)#else#define DLL_NEW_API __declspec(dllexport)#endifDLL_NEW_API void new_test(int * &p); #endif
#include "dll_new.h"void new_test(int * &p){p=new int;*p=12;}
以上為dll的源碼,起名為dll_new.dll
下面是調用該dll的程式:
#include "dll_new.h"#define _CRTDBG_MAP_ALLOC#include <stdlib.h>#include <crtdbg.h>#include <stdio.h>#pragma comment(lib,"dll_new.lib") //在dll中new出來的空間要在dll內部delete掉,如果在調用dll的外部程式中釋放的話,會崩潰void main(){ /***********用於檢測記憶體泄露**************/ //此代碼檢測不出dll裡面的記憶體泄露!!! int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); tmpFlag |= _CRTDBG_LEAK_CHECK_DF; _CrtSetDbgFlag( tmpFlag ); /*************用於檢測記憶體泄露********************/ int *p=NULL; new_test(p); printf("%d\n",*p); delete p;}
如上述程式所示:指標p在dll的函數new_test中申請了空間,但是在外面程式delete的時候,會引起程式崩潰。
原因分析:
模組之間分配和釋放記憶體有一些不安全因素,因為不同模組(特別是不同語言開發的模組)可能使用了不同的記憶體管理機制,這種情況是不是跨模組釋放的,所以VC的Debug版中對記憶體釋放做了檢查,如果發現不是本模組分配的就會報錯。
造成失敗的原因是分配和釋放記憶體不是由相同的堆管理程式完成的,例如動態連結程式庫中的堆在預設情況下是由msvcrt.dll中的堆管理程式管理的 (以動態連結的方式),而exe程式的堆在預設情況下是由程式自己的代碼管理(以靜態連結的方式),由於它們的堆管理程式不同,當動態連結程式庫分配的記憶體在 exe程式中釋放時就會出錯,因為exe程式所在的堆並沒有分配這塊記憶體,而你卻要求它釋放這塊記憶體。
解決方案(三種解決辦法):
1、將程式中所有的模組都連結到C/C++運行期庫Multithreaded DLL,修改後所有分配和釋放堆上記憶體的操作都由同一個堆管理程式管理,這樣便解決了問題。
2、 在DLL中增加一個匯出函數來釋放記憶體。誰負責分配,誰就必須負責釋放!
3、使用智能指標,如std::shared_ptr。這樣不用自己釋放記憶體,智能指標在發現引用計數為1的時候,會自動釋放記憶體。