一個類假如存在一個靜態成員變量指針,在以下幾種情況下動態分配內存,該如何回收內存:
1)在外部函數中動態分配內存,代碼如下:
test.cpp
class Test
{
public:
static char* m_pSZ;
};
char* Test::m_pSZ = NULL;
void testAlloc()
{
Test::m_pSZ = new char[16];
}
int main()
{
testAlloc();
std::cout << "Already alloc for Test::m_pSZ!" << std::endl;
return 0;
}
通過使用g++ -g test.cpp -c test編譯,再g++ -o test test.o鏈接成執行文件,最後通過檢測內存泄漏工具valgrind檢測,命令如下:
valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all ./test
檢測結果如下:
==14924== Memcheck, a memory error detector
==14924== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==14924== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==14924== Command: ./test
==14924==
Already alloc for Test::m_pSZ!
==14924==
==14924== HEAP SUMMARY:
==14924== in use at exit: 16 bytes in 1 blocks
==14924== total heap usage: 1 allocs, 0 frees, 16 bytes allocated
==14924==
==14924== 16 bytes in 1 blocks are still reachable in loss record 1 of 1
==14924== at 0x402ACDB: operator new[](unsigned int) (vg_replace_malloc.c:383)
==14924== by 0x80486EE: testAlloc() (test.cpp:13)
==14924== by 0x8048703: main (test.cpp:18)
==14924==
==14924== LEAK SUMMARY:
==14924== definitely lost: 0 bytes in 0 blocks
==14924== indirectly lost: 0 bytes in 0 blocks
==14924== possibly lost: 0 bytes in 0 blocks
==14924== still reachable: 16 bytes in 1 blocks
==14924== suppressed: 0 bytes in 0 blocks
==14924==
==14924== For counts of detected and suppressed errors, rerun with: -v
==14924== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
從上面結果看,還是有泄露的,泄露類型屬於"still reachable",對於這類型泄露,在網上查了下:
內存泄露可以分爲兩種:
一種是,程序中有指針指向通過malloc或者new申請的內存,但是在程序結束前,一直未收回。如果這種內存一直增加的話,可能導致內存耗盡。不過程序結束後系統會自動回收這些內存。
另一種是,通過malloc或者new申請的內存,但是程序中已經沒有指針指向申請的內存。程序一直在執行,泄露的內存會越來越多,可能會耗盡程序的堆內存。
對於上述的第一種內存泄露的情況,在valgrind的報告中會表示爲“still
reachable”。
對於第二種情況的內存泄露,在valgrind的報告中會表示爲”Directly lost和Indirectly lost”
另外對於valgrind報告中的“possibly lost”,是因爲指針沒有指向到申請堆的開始。如,c++的string有內存池的概念。如果程序中使用了string,且非正常退出(如使用ctrl+c快捷鍵終止程序),會報“possibly
lost”。
從上面分析可知,“still reachable”類型內存泄露,對內存並不影響,程序結束後,會自動回收它們。但也可以顯示的回收該類靜態成員指針內存,直接在main中delete [] Test::m_pSZ即可。
2)在類的靜態成員函數中給靜態成員變量動態分配了內存,應該在哪些地方顯示的回收這些內存呢?在靜態成員函數中嗎?還是任何地方?在一篇博文中博主說:“在類的生命週期中,類的靜態成員不能被delete”.如果在類的生命週期中,delete類的靜態成員,會core,下面用如下代碼測試:
test_3.cpp
#include <iostream>
class B
{
private:
static char* m_pSZ;
public:
inline B()
{
if(NULL == m_pSZ)
{
m_pSZ = new char[8];
}
}
inline ~B()
{
if(m_pSZ != NULL)
{
delete []m_pSZ;
}
}
};
char* B::m_pSZ = NULL;
int main()
{
B b;
std::cout << "正處於類的生命週期中!" << std::endl;
return 0;
}
上面代碼中,m_pSZ肯定和類的生命週期一樣,而對象b的生命週期肯定比m_pSZ短,但並不妨礙,在m_pSZ生命週期之前釋放內存,m_pSZ指針是靜態變量,在內存的全局區域,而該指針指向的內存是堆內存,在可以獲得指向堆中某段內存的指針情況下,爲啥不能釋放,不合規矩。且在實踐中,經過簡單的編譯,運行,並沒有出現dump core現象。
使用valgrind工具檢測結果如下:
valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all ./test_3
==15418== Memcheck, a memory error detector
==15418== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==15418== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==15418== Command: ./test_3
==15418==
正處於類的生命週期中!
==15418==
==15418== HEAP SUMMARY:
==15418== in use at exit: 0 bytes in 0 blocks
==15418== total heap usage: 1 allocs, 1 frees, 8 bytes allocated
==15418==
==15418== All heap blocks were freed -- no leaks are possible
==15418==
==15418== For counts of detected and suppressed errors, rerun with: -v
==15418== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
所以,對於類的靜態成員變量指針動態分配內存,可以在程序結束前的任何能夠訪問靜態成員變量指針的地方,對動態分配內存進行釋放,也可以不顯示釋放,因爲程序結束後,系統會自動回收。
最後,對於test_3.cpp代碼,在類的構造函數中動態分配內存,等於多個對象共享該靜態變量指針,因此若某個對象析構了,釋放了內存,其他對象若還使用該共享的靜態變量指針,就是訪問空指針啦!,故更好的設計是,使用靜態變量維護一個引用計數,每創建一個對象,引用計數加1,析構函數中,引用計數減一,直到爲0才釋放內存。代碼如下:
#include <iostream>
class B
{
private:
static char* m_pSZ;
static unsigned int m_count;
public:
inline B()
{
if(NULL == m_pSZ)
{
m_pSZ = new char[8];
}
++m_count;
std::cout << m_count << std::endl;
}
inline ~B()
{
if(m_pSZ != NULL)
{
--m_count;
if(0 == m_count)
{
delete []m_pSZ;
m_pSZ = NULL;
}
}
std::cout << m_count << std::endl;
}
};
char* B::m_pSZ = NULL;
unsigned int B::m_count = 0;
int main()
{
std::cout << "正處於類的生命週期中!" << std::endl;
B b;
B b1;
B b2;
B b3;
return 0;
}