2013年10月14日月曜日

テンプレートクラスの静的メンバ変数の使用法

クラス内で(アドレスを使用しない組み込み型以外の)静的なメンバ変数を定義した場合、クラス外でも宣言をしないと使用できません。(リンク時にエラーとなります。) (参考サイト: http://www7b.biglobe.ne.jp/~robe/cpphtml/html02/cpp02028.html

テンプレートクラスでも同様で、以下のように書けば問題ありません。

template<int N1, int N2>
class MyClass {
private:
    static int* p;
};
template<int N1, int N2>
int* MyClass<N1, N2>::p;

なお、コンパイル確認はMicrosoft Visual C++ Compiler Nov 2012 CTP (v120_CTP_Nov2012)で行いました。

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