1. 程式人生 > >慎用 new、delete

慎用 new、delete

C++ STL 為我們提供了一套容器。在多數情況下,這套容器已足夠讓我們使用。所以,需要我們自己去用 new/new []/delete/delete [] 來管理記憶體的必要性並不是很大。此外,自己管理記憶體極容易導致程式出錯。記憶體洩漏、堆遭到破壞這些事情都有可能發生。雖然各種程式設計 Tips 我們都熟稔於心,但真正在實際專案開發中遇到過的才令人印象深刻。

為什麼不要自己管理記憶體?讓我們重溫這幾條:
1. STL containers 已經經過良好設計,而且能滿足大多數情況下的需求。
2. 只 new/new[] 未 delete/delete[] 或者 new/new[] 之後修改了得到的記憶體首地址,會導致記憶體洩漏。
3. 如果在一個類的實現中使用了 new/new[]、delete/delete[] ,而該類又沒有設計良好的複製控制函式時,在標準容器中使用該類是危險的。

今天就結合實際專案經歷強調一下第三點(很多書籍中也強調過這一點,但實際中未被坑過印象就不深刻啊)。
今天維護一個專案時,需要給4個通道的訊號新增一步濾波處理。該濾波演算法是一個同事做的,並以類的方式提供給我使用。下面是該類的大致程式碼:
class CFilter
{
public:
    CFilter(int len) {
        data_ = new double[len];
    }

    ~CFilter() {
        delete[] data_;
    }

    // ... 省略演算法實現部分。
private:
    double *data_;    
};
由於該類會根據每個通道的噪聲特徵調整類內部的一些引數,而每個通道的噪聲特徵都不一樣,所以需要給每個通道都建立一個 CFilter 型別的物件。我是這樣做的:
std::vector<CFilter> filters(4, 1000);
其中1000是傳遞給建構函式的引數。

這段程式碼是很危險的。因為標準容器的一些操作會涉及到元素的拷貝。而由於類的設計者未為 CFilter 型別提供拷貝建構函式和賦值建構函式(也就是未作任何複製控制),所以拷貝 CFilter 型別的物件時,其內部的 data_ 指標也會被拷貝,其後果可想而知。

在 VS2010 中,這段程式碼導致的錯誤就是程序堆被破壞,而且每次的出錯地方都不一樣。

解決方案:

1. 為類 CFilter 設計拷貝建構函式和複製建構函式,控制它的這些行為。

2. 把類 CFilter 類設計成 noncopyable 的。當然,這樣可能就無法把 CFilter 型別的物件儲存在標準容器中了。