C++ 動態配置記憶體心得(下)

續上集...

  • STL container 的 clear 真的就 clear 乾淨了嗎?

    相信很多人在寫 C++ 時,都會想利用 STL 來省去一些造輪子的麻煩,其中最常被使用的就是像 std::vectorstd::list 這類的 container,常常有人就拿它們來當 variable-length 的 array 來解決問題。

    然後可能就會有像這樣子的 code 出現:
    [code lang="cpp"]
    ...
    std::vector vec;
    ...
    MyClass* pObj = new MyClass(...);
    vec.push_back(pObj);
    ...
    ...
    // 用完 vec 之後,試圖把它清除掉
    vec.clear();
    [/code]
    很多人以為這樣寫,clear() method 就會幫你把曾經放進去的 element 給清乾淨了,可惜並不是這麼美好!沒錯,clear() 的確是會把 vec 裡的 elements 都清掉沒錯,但你有沒有注意到,vec 的 element type 是 MyClass*,是個指標的資料型態,也就是說:呼叫 clear() method 清掉了這些個指標,但卻沒有清掉指標所指到的 object!所以最起碼你應該這麼寫:
    [code lang="cpp"]
    // 以 iterator 走訪整個 vec 裡的元素,
    // 然後一一 delete 掉指到的 object
    for (std::vector::iterator iter = vec.begin(); iter != vec.end(); ++iter)
    {
    delete *iter;
    }
    vec.clear();
    [/code]
    如果你有大量使用 STL 習慣的人,一定要小心 container 裡的 element 是不是真的都清乾淨了。

  • 如果你很懶,請愛用 std::auto_ptr

    之前有提到 new 過就不要忘記 delete 它,但如果你常常忘記 delete 的話,不妨用 std::auto_ptr 來幫你吧!std::auto_ptr 是一個 smart pointer ,它雖然是一個 templated class,但它的 object 用起來就好像一般的指標一樣,差別在於:當這個 object 被 destroy 時,它會檢查指到的記憶體是不是經由 new 所配置來的,如果是,就順便清掉!下面就用簡單的例子來說明:
    [code lang="cpp"]
    #include // 要引入 memory 標頭檔
    ...
    {
    std::auto_ptr pObj(new MyClass());
    ...
    // 這時就可以使用 pObj->xxx() 來呼叫 method,
    // 就好像你用 MyClass *pObj; 宣告一樣。
    ...
    // 此時 pObj 被 destroy 了,
    //而它指到的記憶體也會被釋放。
    }
    ...
    [/code]
    看起來方便多了,是吧?不過沒用過 std::auto_ptr 的人要注意到,它是沒辦法經由 copy assignment 來與另一個 std::auto_ptr object 來 share 指到的記憶體位置。比方說下列的 code 就會使 p1 最後指到 null
    [code lang="cpp"]
    ...
    std::auto_ptr p1(new MyClass());
    std::auto_ptr p2;
    p2 = p1;
    ...
    [/code]
    這樣 p2 會指到原本 p1 指到的記憶體,但卻讓 p1 指到 null 了。如果你想要使用可以 share 的 smart pointer,或許你可以參考 boost library 裡的 shared_pointer,或是等待 C++ 2.0 的標準函式庫(詳情可見 TR1 draft

簡單地寫了最近常碰到的問題,歡迎各方高手不吝賜教。:)

  • doomleika

    不好意思,我記得autoptr會無差別delete他所存的指標耶@@

    #include
    #include

    class Dummy
    {
    public:
    Dummy()
    {
    std::cout << “A dummy created” << std::endl;
    }
    ~Dummy()
    {
    std::cout << “Dummy deleted” << std::endl;
    }
    };

    void dummyFun(Dummy* ptrDummy)
    {
    std::cout << “Function started” << std::endl;

    std::auto_ptr aPtr(ptrDummy);

    std::cout << “Funtion ended” << std::endl;
    }

    int main()
    {
    std::cout << “Program started” << std::endl;
    Dummy iAmDummy;
    dummyFun(&iAmDummy);

    std::cout << “Program ended” << std::endl;
    return 0;
    }

    在VC2008 express中如果在debug build中在auto_ptr嘗試delete從main傳過去的資料VC2008會自動break報告問題出現,而在release build中在dummyFun會被delete一次而離開main再destruct一次。

    是因為這是VC2008用的政策嗎?

  • 這…VC對 STL 的實作我一直都覺得很神妙,也許我沒辦法回答您 🙁

  • 抱歉,我不應該在這邊貼code的orz

    http://rafb.net/p/vnAGy329.html

  • 因為 debug build 會給比較嚴謹的 memory routine.
    你嘗試因為 auto_ptr 去 delete heap-based 的指標,會給錯誤
    而 release build 就沒有那麼嚴謹,通常簡單的程式會繼續跑下去 直到發生到不可回復的錯誤為止。

  • doomleika

    天啊!二年以前的問題竟然有人回答!謝謝你!