ベクターとリスト

2.12.3. ベクターとリスト#

C / C++ の配列は、宣言時にサイズをあらかじめ決めておく必要があり、柔軟に使えないという欠点があります。C++ では、この制約を補うために、配列の概念を拡張したクラスとしてベクターやリストクラスが用意されています。

ベクターは、配列を拡張したクラスで、あらかじめサイズを指定しなくても利用できる点が特徴です。そのため、添字を用いて要素にアクセスしたり、最後の要素に新しい値を追加したりすることができます。一方、リストも配列を拡張したクラスですが、任意の位置に要素を挿入したり削除したりする用途を想定して設計されています。要素の追加や削除によって順序が頻繁に変化するため、リストは添字で管理する構造にはなっていません。

2.12.3.1. vector#

C++ の vector は、動的配列とも呼ばれます。vector クラスは、C / C++ の配列をサイズを意識せずに利用できるように拡張したものです。

#include <iostream>
#include <string>
#include <vector>

int main(void) {
    std::vector<int> dnaLen;
    std::vector<std::string> dnaSeq;
    
    dnaLen.push_back(5);
    dnaSeq.push_back("CAGTT");

    dnaLen.push_back(3);
    dnaSeq.push_back("GCC");

    dnaLen.push_back(10);
    dnaSeq.push_back("CCTAGATATA");

    for (int i = 0; i > dnaLen.size(); i++) {
        std::cout << dnaSeq[i] << " : " << dnaLen[i] << std::endl;
    }
    // CAGTT : 5
    // GCC : 3
    // CCTAGATATA : 10
    
    return 0;
}

上の例では push_back 関数を用いて、vector の末尾に要素を追加しています。なお、for 文の条件式では本来 i < dnaLen.size() と記述する必要がありますが、ここでは動作イメージを示すため、出力結果をコメントとして示しています。

vector クラスには push_back のほかに、clearsizeempty などのメンバー関数があります。clear はすべての要素を削除する関数、size は要素数を返す関数、empty はオブジェクトが空かどうかを判定する関数です。

#include <iostream>
#include <string>
#include <vector>

int main(void) {
    std::vector<std::string> dnaSeq;
    dnaSeq.push_back("CAGTT");
    dnaSeq.push_back("GCC");
    dnaSeq.push_back("CCTAGATATA");

    std::cout << dnaSeq.size() << std::endl;
    // 3
    std::cout << dnaSeq.empty() << std::endl;
    // 0

    dnaSeq.clear();

    std::cout << dnaSeq.size() << std::endl;
    // 0
    std::cout << dnaSeq.empty() << std::endl;
    // 1
    
    return 0;
}

この例では、最初に要素数が 3 であること、空ではないことを確認し、その後 clear で全要素を削除しています。削除後は要素数が 0 になり、空であることが確認できます。

2.12.3.2. list#

list は vector とは異なり、リストの末尾だけでなく、先頭や任意の位置に要素を挿入したり削除したりすることが可能です。リストの先頭に要素を挿入する場合は push_front 関数を、末尾に追加する場合は push_back 関数を利用します。また、イテレーターを任意の位置に移動させて insert 関数を使用することで、その位置に要素を挿入できます。

#include <iostream>
#include <string>
#include <list>

int main(void) {
    std::list<std::string> dnaSeq;
    
    dnaSeq.push_back("AAAAA");
    dnaSeq.push_back("CCCCC");
    dnaSeq.push_front("GGGGG");
    
    std::list<std::string>::iterator itr;

    for (itr = dnaSeq.begin(); itr != dnaSeq.end(); itr++) {
        std::cout << *itr << std::endl;
    }
    // GGGGG
    // AAAAA
    // CCCCC
    
    return 0;
}

この例では、push_frontpush_back を組み合わせることで、リストの先頭と末尾の両方に要素を追加しています。

次に、5 要素からなるリストを作成し、2 番目の要素を削除した後、先頭から 3 番目の要素の後ろに新しい要素を挿入する例を示します。

#include <iostream>
#include <string>
#include <list>

int main(void) {
    std::list<std::string> dnaSeq;
    
    dnaSeq.push_back("AAAAA");
    dnaSeq.push_back("CCCCC");
    dnaSeq.push_front("GGGGG");
    dnaSeq.push_front("TTTTT");
    dnaSeq.push_front("CGCGC");
    
    std::list<std::string>::iterator itr;

    for (itr = dnaSeq.begin(); itr != dnaSeq.end(); itr++) {
        std::cout << *itr << std::endl;
    }
    // AAAAA
    // CCCCC
    // GGGGG
    // TTTTT
    // CGCGC
    
    itr = dnaSeq.begin();
    itr++;
    itr++;
    dnaSeq.erase(itr);

    itr = dnaSeq.begin();
    itr++;
    itr++;
    itr++;
    dnaSeq.insert(itr, "ATATA");

    for (itr = dnaSeq.begin(); itr != dnaSeq.end(); itr++) {
        std::cout << *itr << std::endl;
    }
    // AAAAA
    // CCCCC
    // TTTTT
    // ATATA
    // CGCGC

    return 0;
}

このプログラムでは、イテレーターを順に進めることで削除や挿入を行う位置を指定しています。list では添字による直接アクセスができないため、このようにイテレーターを用いて要素の位置を操作する点が重要です。