關於類靜態成員變量指針通過動態分配的內存如何回收的探討

一個類假如存在一個靜態成員變量指針,在以下幾種情況下動態分配內存,該如何回收內存:

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;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章