2014-10-17 33 views
1

我正在處理一個C++庫的包裝,我需要使用plain C.該庫具有一個名爲checkError()的函數。該函數會引發與對象內發生的錯誤相關的異常。在C++包裝器中,我捕獲了這個錯誤,mallocing一個C兼容的結構,將錯誤消息和信息分配給錯誤結構變量,然後我將指針傳遞迴結構。這一切都很好,但是我一直擔心C調用函數處理完結構後會發生內存泄漏。釋放包含malloc'd字符串的malloc'd結構

對於這個問題,我們假設該庫的對象稱爲objectA和圖書館被稱爲libraryA這裏基本上是我有:

的C & C++兼容wrapper.h

#ifndef LIBRARY_WRAPPER_DEFINITION 
#define LIBRARY_WRAPPER_DEFINITION 

#ifdef __cplusplus 
extern "C" { 
#endif 

typedef struct wrapper_error_message_struct{ 
    char *what; 
    char *typeAsText; 
    unsigned char severityLevel; /* 0-10; 0 = lowest severity. 10 = highest severity */ 
} wrapper_error_message_t; 

typedef void *pLibObj_t; 

/** 
* I've omitted the other prototypes, typedefs, etc. 
**/ 

/* checkError()'s wrapper prototype */ 
wrapper_error_message_t *wrapper_check_error(pLibObj_t ptrObjectToCheck); 

#ifdef __cplusplus 
} 
#endif 
#endif 

C++實現的wrapper_check_errorwrapper.cpp

/* Imports and other functions preceding wrapper_check_error() */ 
wrapper_error_message_t *wrapper_check_error(pLibObj_t ptrObjectToCheck){ 
    /* for the sake of this question, I've omitted the validity checks on 
    * ptrObjectToCheck. I've also omitted my malloc checks. */ 
    libraryA::objectA *convertedObj = (libraryA::objectA *)ptrObjectToCheck; 
    size_t structSize = sizeof(wrapper_error_message_t); 
    size_t charSize = sizeof(char); // just in case it's not 1 on this system. 
    try{ 
     convertedObj->checkError(); 
     /* if the checkError() function did not throw an error, then we 
      can simply return NULL indicating that no ERROR was thrown */ 
     return NULL; 
    }catch(libraryA::SomeLibraryExceptionType_1 &err){ 
     wrapper_error_message_t *cErr = (wrapper_error_message_t *)malloc(structSize); 

     cErr->severityLevel = 3; 

     const char *errWhat = err.what(); 
     cErr->what = (char *)malloc((strlen(errWhat)+1) * charSize); 
     strcpy(cErr->what, errWhat); 

     const char errorType[] = "Library Exception Type Name 1"; 
     cErr->typeAsText = (char *)malloc((strlen(errorType)+1) * charSize); 
     strcpy(cErr->typeAsText, errorType); 
     return cErr; 

    }catch(libraryA::SomeLibraryExceptionType_2 &err){ 
     /* Roughly the same as SomeLibraryExceptionType_1's catch statement */ 

    }catch(libraryA::SomeLibraryExceptionType_3 &err){ 
     /* Roughly the same as SomeLibraryExceptionType_1's catch statement */ 

    }catch(std::exception &err) 
     wrapper_error_message_t *cErr = (wrapper_error_message_t *)malloc(structSize); 
     cErr->severityLevel = 7; 
     const char *errWhat = err.what(); 
     cErr->what = (char *)malloc((strlen(errWhat)+1) * charSize); 
     strcpy(cErr->what, errWhat); 

     const char errorType[] = "Unknown standard exception (std::exception)"; 
     cErr->typeAsText = (char *)malloc((strlen(errorType)+1) * charSize); 
     strcpy(cErr->typeAsText, errorType); 
     return cErr; 

    }catch(...){ 
     wrapper_error_message_t *cErr = (wrapper_error_message_t *)malloc(structSize); 
     cErr->severityLevel = 10; 
     cErr->what = NULL; 

     const char errorType[] = "Unknown. Could not be caught."; 
     cErr->typeAsText = (char *)malloc((strlen(errorType)+1) * charSize); 
     strcpy(cErr->typeAsText, errorType); 
     return cErr; 
    } 
} 

這是在main.c使用wrapper_check_error()的普通C函數:

/* imports, irrelevant functions, irrelevant variable definitions */ 
void someFunction(){ 
    pLibObj_t ourWrappedObj; 

    /* 
    * function code before error check. 
    */ 

    wrapper_error_message_t *errorMsg = wrapper_check_error(ourWrappedObj); 
    if(wrapper_error_message_t != NULL){ 
     /* there was an error. 
     * the code within this if statement: 
     *  - processes the error message 
     *  - logs information about it (current time, type, severity and the what message) 
     *  - makes logical decisions about how to handle it if possible. 
     */ 
     free(errorMsg); 
     errorMsg = NULL; 

     /* In the line above, the function frees the malloc'd structure to remove it 
     * from the heap. 
     * 
     *  This free statement is what I'm concerned about. 
     * 
     */ 
    } 
    // etc. 
} 

威爾free(errorMsg)還免費char *whatchar *typeAsText因爲他們的結構構件,其被釋放?根據我所做的一些閱讀,我目前相信whattypeAsText指向的值仍將存在於堆上,因爲errorMsg只包含指向這些值的指針而非值本身。

如果*what*typeAsText仍然在堆上,我會寫一個函數來釋放結構成員,然後釋放結構本身。但是,如果有必要,我只想這樣做。

如果有人可以提供一些關於此的指導/見解,將不勝感激。

謝謝。


如果這個問題是重複的,我很抱歉。如果是這樣,請將問題指向類似問題的方向,以便我可以閱讀那裏的答案。我搜索了以及其他網站,但我還沒有找到任何答案我的問題。

我從我的項目中摘錄了代碼,縮短了代碼,並重命名了變量/函數。除了一些錯誤檢查之外,如果代碼中存在明顯的錯誤,我不知何故,請發表評論,以便我可以修改它。如果有什麼不清楚的地方,請在評論中告訴我,我會盡我所能來更新問題並進行澄清。

+3

'free'不是遞歸(這是不可能的,它無法知道的方式你有其他的指向內部塊,並希望保持它們)。您必須手動釋放成員指針指向的塊。 – 2014-10-17 18:57:10

+0

謝謝,很高興知道。我知道'free'接收一個'void *'作爲參數,所以這讓我更加不確定是否釋放了整個結構。也就是說,如果C++包裝器是用託管C++實現而不是本地編寫的,情況會不會改變?例如,如果包裝器是用MSVC而不是本地C++編寫的,那麼釋放結構本身就成爲一種有效的方法來刪除其中的C字符串?我只問,因爲MSVC有一個內置的垃圾收集器。我計劃使用本機C++來保持項目,所以我只是將MSVC作爲一個學術問題。 – SpencerD 2014-10-17 19:05:53

回答

3

既然有用於構建你傳遞所有權的對象具有三個malloc()電話:

wrapper_error_message_t *cErr = (wrapper_error_message_t *)malloc(structSize); 

    cErr->severityLevel = 3; 

    const char *errWhat = err.what(); 
    cErr->what = (char *)malloc((strlen(errWhat)+1) * charSize); 
    strcpy(cErr->what, errWhat); 

    const char errorType[] = "Library Exception Type Name 1"; 
    cErr->typeAsText = (char *)malloc((strlen(errorType)+1) * charSize); 
    strcpy(cErr->typeAsText, errorType); 
    return cErr; 

有將需要3調用free()

free(errorMsg->typeAsText); 
    free(errorMsg->what); 
    free(errorMsg); 

作爲一個邊注意,sizeof(char)是1的定義,所以沒有必要爲此做任何特殊的事情。

而作爲另一個方面說明,我建議使用strdup()代替容易出錯的混亂,如:

cErr->what = (char *)malloc((strlen(errWhat)+1) * charSize); 
    strcpy(cErr->what, errWhat); 
+0

非常感謝您澄清這一點。另外,我從來沒有聽說過'strdup',但那比我所做的要好得多。我讀過一些使用'sizeof(char)'的代碼,所以我一直在使用它,因爲作者聲明char可以大於1。也許他的意思是一些較舊的系統可能有超過8位的字符。無論如何,我在問題的評論中問過,但如果這個項目是用託管的C++實現(如MSVC)編寫的,那麼內置的垃圾收集器會照顧成員嗎?我堅持本地C++,但我只是好奇。 – SpencerD 2014-10-17 19:18:59

+1

我對C++/CLI(或今天調用的任何託管C++)並不是很熟悉,但我的期望是使用'malloc()'分配的任何東西都不會被託管垃圾收集器處理。如果使用'gcnew'分配,那麼託管垃圾收集器應該處理它。 – 2014-10-17 19:24:36