Gobble up pudding

プログラミングの記事がメインのブログです。

MENU

STL入門 第3回 ~list編~

スポンサードリンク

f:id:fa11enprince:20150731121156j:plain
過去のSTLの記事はこちら↓


入門記事というよりただの自分の勉強記録になり下がってるのは仕様です。

listはSTLコンテナでvectorに似ています。シーケンシャルアクセスをサポートしています。前からも後ろからも。ただし、vectorのようなランダムアクセスはサポートしていません。要は配列のようにインデックスを指定してアクセスできないってことです。ベクターさんは後ろからパコパコbackが専門でしたが(あっ、途中からぶちこむinsertもあるよ!)、リストさんは前からもできちゃいます(*´Д`)!push_front!!insert!!
いや、vectorも前からinsertできるじゃないか?というのがありますが、そんなことしてしまうと、全部コピーしちゃうかららめぇ。えっ?でもinsertしたいんだよ!いれさせろよ!!(*´Д`)listにはmerge()やsplice()など便利なものがあります。今回はmerge()を紹介します。ソート済みの要素をくっつけてちゃんと整列されてるじゃん!っていう優れものです。
ちなみにlist::sort()はvector::sort()とは別物です。
下記では小数の比較用に独自クラスを作成しています。その際operator<()を定義しなくてはなりません。なお、演算子のオーバーロードですが、クラス内では二項演算子の場合引数は右辺のみです。たまにあれれってなります。クラス内の単項演算子の場合は引数なしです。クラスが2つあって、2クラス混ぜてその身長順に並べるってものです。

list::merge()のサンプル

#include <list>
#include <algorithm>
#include <iostream>

using namespace std;

// ソートするための型を定義
// doubleの大小関係を定義(そのまま比較できないから無理やりintになおす)
class Height {
public:
    explicit Height(double h) { h_ = h; }
    double get() { return h_; }
    // ※クラス内だから二項演算子は1つだけ必要
    bool operator<(const Height& rhs)
    {
        return static_cast<int>(h_*100) < static_cast<int>(rhs.h_*100);
    }
private:
    double h_;
};

int main()
{
    list<Height> classA = { Height(171.11), Height(172.38), Height(158.01) };
    list<Height> classB = { Height(148.09), Height(181.02), Height(173.91) };
    // マージする前にソートする
    classA.sort();
    classB.sort();
    classA.merge(classB);
    for (auto &h : classA)
    {
        cout << " " << h.get();
    }
    cout << "\n";
    return 0;
}

実行結果

 148.09 158.01 171.11 172.38 173.91 181.02

ちゃんと整列されてますね。

ちなみにコンテナに動的確保した領域やそのポインタ入れたら解放どうするの?っていう話です。

std::vector<Foo *> vec;
vec.push_back(new Foo());

とか

std::vector<Foo *> vec;
Foo *foo = new Foo();
vec.push_back(foo);

とやった場合です。この場合、new Foo()で確保したものをやっぱり解放してやらないと確保し続けます。子供を産んだら面倒見ましょう。産みっぱなしはダメですよってやつですね。

コンテナにいれた領域を解放

#include <iostream>
#include <list>

using namespace std;

class Pokemon
{
public:
    Pokemon()
    {
        cout << "> ポケモン を つくりました" << endl;
    }
    virtual ~Pokemon()
    {
        cout << "> ポケモン を けしました" << endl;
    }
    virtual void attack() = 0;
    virtual string name() = 0;
    int getHp() { return hp_; }
protected:
    int hp_;
};

class Hitokage : public Pokemon
{
public:
    explicit Hitokage(int hp)
    {
        hp_ = hp;
        cout << ">> ヒトカゲ を つくりました" << endl;
    }
    ~Hitokage() override
    {
        cout << ">> ヒトカゲ を けしました" << endl;
    }
    void attack() override
    {
        cout << "ひのこ で こうげき" << endl;
    }
    string name() override { return "ヒトカゲ"; }
};

class Pikachu : public Pokemon
{
public:
    explicit Pikachu(int hp)
    {
        hp_ = hp;
        cout << ">> ピカチュウ を つくりました" << endl;
    }
    ~Pikachu() override
    {
        cout << ">> ピカチュウ を けしました" << endl;
    }
    void attack() override
    {
        cout << "10まんボルト で こうげき" << endl;
    }
    string name() override { return "ピカチュウ"; }
};

int main()
{
    list<Pokemon *> pokemon;
    pokemon.push_back(new Hitokage(39));
    pokemon.push_back(new Pikachu(35));
    for (auto it = pokemon.begin(); it != pokemon.end(); ++it)
    {
        cout << (*it)->name() << "のHP は" << (*it)->getHp() << endl;
        (*it)->attack();
    }
    // 領域を解放-> 作ったら消しましょう
    for (auto it = pokemon.begin(); it != pokemon.end(); ++it)
    {
        delete *it;
    }
    return 0;
}

実行結果

> ポケモン を つくりました
>> ヒトカゲ を つくりました
> ポケモン を つくりました
>> ピカチュウ を つくりました
ヒトカゲのHP は39
ひのこで こうげき
ピカチュウのHP は35
10まんボルト で こうげき
>> ヒトカゲ を けしました
> ポケモン を けしました
>> ピカチュウ を けしました
> ポケモン を けしました

例がアレでその設計どーよっていうのはかなりあると思いますが……。それはおいておいて、上記のようにポリモーフィズムを使いたい場合じゃない限り、ポインタ入れるのやめましょう。せめて生ポ使うなってところですかね。いやでも、国民は健康で文化的な最低限度の生活を営む権利を有するんだよ!!!
それにはスマポのshared_ptrを使うのがいいと思います。上記の例もスマポで置き換えられます。

スマートポインタを使った例(抜粋)

#include <memory>

// ...... snip

int main()
{
    list<shared_ptr<Pokemon>> pokemon;
    pokemon.push_back(shared_ptr<Pokemon>(new Hitokage(39)));
    pokemon.push_back(shared_ptr<Pokemon>(new Pikachu(35)));
    for (auto it = pokemon.begin(); it != pokemon.end(); ++it)
    {
        cout << (*it)->name() << "のHP は" << (*it)->getHp() << endl;
        (*it)->attack();
    }
    return 0;
}