2013年10月6日日曜日

ポインタを格納したvectorの重複要素をメモリリークせずに削除する方法

C++で、deleteを自動で行うuniqueを実装することでstd::vectorの重複要素が指すメモリ領域を解放する方法です。

vector<MyClass*> arr に対して重複した要素を以下のように単にuniqueとeraseを使用して削除すると、vectorから削除された重複要素のメモリが解放されません。

std::sort(arr.begin(), arr.end(), my_smaller);
auto end_it = unique(arr.begin(), arr.end());
fields.erase(end_it, arr.end());
fields.shrink_to_fit(); 
// ここで重複要素(ポインタ)のメモリは解放されますが、
// そのポインタが指すメモリ領域は解放されません

vector<shared_ptr<MyClass>> arr としても上記やり方ではメモリが解放されませんでした。 (なお、sortでは代入操作が行われるためvector<unique_ptr<MyClass>> arr は使用できません。)

そこで、重複要素をvectorから削除する際に重複要素の指すメモリを解放するuniqueを実装することにします。 uniqueの実装は下記リンクに書かれているので、これを改変してp_uniqueを実装しました。 http://www.cplusplus.com/reference/algorithm/unique/

template <typename T, typename ForwardIterator>
ForwardIterator p_unique (ForwardIterator first, ForwardIterator last)
{
    if (first==last) return last;
    ForwardIterator result = first;
    while (++first != last) {
        // ここで重複要素のメモリを解放する
        if(**result == **first) delete (T*)*first;
        else *(++result)=*first;
    }
    return ++result;
}

これを使用すると、下記のようにポインタを格納したvectorをソートして重複したオブジェクトを削除し、不要になったメモリを解放することができます。

vector<MyClass*> arr;
[arrにデータを入れる処理]
std::sort(arr.begin(), arr.end(), my_smaller);
auto end_it = p_unique<MyClass>(arr.begin(), arr.end());
fields.erase(end_it, arr.end());
fields.shrink_to_fit();

my_smallerはポインタの値でなくそれが指すオブジェクトの値で比較するための関数です。

bool my_smaller(const MyClass *e1, const MyClass *e2)
{
    return *e1 < *e2;
}

また、sortとuniqueを使用するには、MyClassで以下の2つの演算子をオーバーロードしておく必要があります。

bool operator < (const MyClass &e);
bool operator == (const MyClass &e);

0 件のコメント:

コメントを投稿