題目不知道怎么取才好,但是意思很簡單:
如果你暴露一個復雜對象給Lua,實現類似於OO編程的話,那么也要把析構函數暴露給Lua.
否則的話,lua gc的時候,回收垃圾對象,沒有找到回收函數,就直接free掉了,這在C++中,是相當致命的.
tolua++中的tolua_cclass函數,用來注冊lua對象,
TOLUA_API void tolua_cclass (lua_State* L, const char* lname, const char* name, const char* base, lua_CFunction col)
同時會把最后的那個參數col,注冊到lua對象的元表里面:
static void push_collector(lua_State* L, const char* type, lua_CFunction col) { /* push collector function, but only if it's not NULL, or if there's no collector already */ if (!col) return; luaL_getmetatable(L,type); lua_pushstring(L,".collector"); //.... lua_pushcfunction(L,col); //....
而發生gc的時候,class_gc_event函數會去在lua對象的元表里面找".collector"這個key,如果沒找到,就用default的析構,否則就用用戶提供的析構函數:
top = lua_gettop(L); if (tolua_fast_isa(L,top,top-1, lua_upvalueindex(2))) /* make sure we collect correct type */ { /*fprintf(stderr, "Found type!\n");*/ /* get gc function */ lua_pushliteral(L,".collector"); lua_rawget(L,-2); /* stack: gc umt mt collector */ if (lua_isfunction(L,-1)) { /*fprintf(stderr, "Found .collector!\n");*/ } else { lua_pop(L,1); /*fprintf(stderr, "Using default cleanup\n");*/ lua_pushcfunction(L,tolua_default_collect);//這個是默認的析構函數 } lua_pushvalue(L,1); /* stack: gc umt mt collector u */ lua_call(L,1,0);
而默認的析構函數是C free的簡單封裝:
TOLUA_API int tolua_default_collect (lua_State* tolua_S) { void* self = tolua_tousertype(tolua_S,1,0); free(self); return 0; }
如果你通過tolua++注冊一個復雜類型給lua的話,析構函數不被調用,而直接調用了free,會發生很多未定義行為.
這就是在我們服務器中隱藏了超過兩年的一個Bug......