標準C++ベクタ

配列型文字列と比べるとC++ストリング型オブジェクトは使いやすく実行エラーの起こる回数が少ない.これと同じことが配列と標準C++ベクタ型オブジェクトの間でもいえる.つまり,C++ベクタオブジェクトは配列をもっと使いやすくしたものである.vectorクラステンプレートは標準C++ライブラリのコンテナのプロトタイプである.

vectorクラステンプレートは$<$vector$>$ヘッダで定義されている.

例題 14..1   次のプログラムは,8個の文字列を持つベクタとそれらを呼び出すload()関数,そして表示するprint()関数を構築している.実行するとどんな結果が得られるか.

#include <iostream>
#include <string>
#include <vector>
using namespace std;
void load(vector<string>&);
void print(vector<string>);
const int SIZE = 8;

int main()
{
    vector<string> v(SIZE);
    load(v);
    print(v);
}

void load(vector<string>& v)
{
    v[0] = "Japan";
    v[1] = "America";
    v[2] = "Austria";
    v[3] = "Germany";
    v[4] = "England";
    v[5] = "Italy";
    v[6] = "Spain";
    v[7] = "Korea";
}

void print(vector<string> v)
{
    for(int i=0; i < SIZE; i++){
        cout << v[i] << endl;
    }
}

解答 vector$<$string$>$v(SIZE)で8個のストリング型のベクタを用意する.load()関数で国名を入力し,print()関数で出力している.

実行結果

\begin{figure}% latex2html id marker 4022
\centering
\includegraphics[width=8.0cm]{CPPPIC/reidai14-1.eps}
\end{figure}

例題 14..2   例題14-1と同じものをvector$<$string$>$の代わりに型認識子 strings,v[i]の代わりにpush_back()関数,大域定数 SIZEの代わりにsize()関数を用いて作成せよ.

解答

#include <iostream>
#include <string>
#include <vector>
using namespace std;
typedef vector<string> Strings;
void load(Strings&);
void print(Strings);
const int SIZE = 8;

int main()
{
    Strings v;
    load(v);
    print(v);
}

void load(Strings& v)
{
    v.push_back("Japan");
    v.push_back("America");
    v.push_back("Austria");
    v.push_back("Germany");
    v.push_back("England");
    v.push_back("Italy");
    v.push_back("Spain");
    v.push_back("Korea");
}

void print(Strings v)
{
    for(int i=0; i < v.size(); i++){
        cout << v[i] << endl;
    }
}

ここで,ベクトルvは宣言された段階では要素を持っていない.push_back()関数が呼ばれるたびに,ベクトルvの最後に新しい要素を付け加え,そのサイズを増やす.したがって,load()関数が積み終えたときにはベクトルvの値は8になっている.出力は例題14-1と同じである.

ベクタの反復子(Iterators on Vectors)

C++標準ライブラリでコンテナや非数値アルゴリズムは,シーケンス(sequence)と呼ばれる概念に焦点をあて,反復子によってシーケンスを操作する方法を用いている. シーケンスという概念を図で表現すると次のようになる.

\begin{figure}\centering
\includegraphics[width=11.5cm]{CPPPIC/fig14-2.eps}
\end{figure}
シーケンスは先頭(begin)と末尾(end)を持っている.反復子は1つの要素を参照し,反復子にシーケンスの中の次の要素を参照させるための演算を提供している.シーケンスの末尾は,シーケンスの最後の要素の次を参照する反復子である.

例題 14..3   ベクタ反復子の利用
次のプログラムを実行するとどんな結果が得られるか調べよ.

#include <iostream>
#include <string>
#include <vector>
using namespace std;
typedef vector<string> Strings;
typedef Strings::iterator Sit;
void load(Strings&);
void print(Strings);

int main()
{
    Strings v;
    load(v);
    print(v);
}

void load(Strings& v)
{
    v.push_back("Japan");
    v.push_back("America");
    v.push_back("Austria");
    v.push_back("Germany");
    v.push_back("England");
    v.push_back("Italy");
    v.push_back("Spain");
    v.push_back("Korea");
}

void print(Strings v)
{
    for(Sit it=v.begin(); it != v.end(); it++){
        cout << *it << endl;
    }
    cout << endl;
}

解答 forループは反復子itをベクタvの始まりに設定することで初期化を行っている.*itは反復子が指している要素を返す.it++で反復子を増加させる.it == v.end()となったら反復子itはベクトルの最後の要素に続く仮の場所に移動したことになる.

実行結果

\begin{figure}% latex2html id marker 4043
\centering
\includegraphics[width=8.0cm]{CPPPIC/reidai14-3.eps}
\end{figure}

例題 14..4   汎用ソートアルゴリズム
標準C++汎用アルゴリズムsort()関数はヘッダ$<$algorithm$>$で定義されている.汎用sort()関数は2つの反復子を引数として必要とする.最初の反復子から最後の反復子までがソートの対象になる.したがって,次のプログラムはベクタvをアルファベット順にソートする.

#include <iostream>
#include <string>
#include <vector>
using namespace std;
typedef vector<string> Strings;
typedef Strings::iterator Sit;
void load(Strings&);
void print(Strings);

int main()
{
    Strings v;
    load(v);
    sort(v.begin(),v.end());
    print(v);
}

void load(Strings& v)
{
    v.push_back("Japan");
    v.push_back("America");
    v.push_back("Austria");
    v.push_back("Germany");
    v.push_back("England");
    v.push_back("Italy");
    v.push_back("Spain");
    v.push_back("Korea");
}

void print(Strings v)
{
    for(Sit it=v.begin(); it != v.end(); it++){
        cout << *it << endl;
    }
    cout << endl;
}

実行結果

\begin{figure}% latex2html id marker 4053
\centering
\includegraphics[width=8.0cm]{CPPPIC/reidai14-4.eps}
\end{figure}

代入ベクタ(Assignment Vectors)

例題 14..5   代入演算子を用いたベクタの複写
次のプログラムでは,ベクタに別のベクタを代入する.

#include <iostream>
#include <string>
#include <vector>
using namespace std;
typedef vector<string> Strings;
tyoedef Strings::iterator Sit;
void load(Strings&);
void print(Strings);

int main()
{
    Strings v, w;
    load(v);
    w = v;
    sort(v.begin(),v.end());
    print(v);
    print(w);
}

void load(Strings& v)
{
    v.push_back("Japan");
    v.push_back("America");
    v.push_back("Austria");
    v.push_back("Germany");
    v.push_back("England");
    v.push_back("Italy");
    v.push_back("Spain");
    v.push_back("Korea");
}

void print(Strings v)
{
    for(Sit it=v.begin(); it != v.end(); it++){
        cout << *it << endl;
    }
    cout << endl;
}

実行結果

\begin{figure}% latex2html id marker 4062
\centering
\includegraphics[width=7.7cm]{CPPPIC/reidai14-5.eps}
\end{figure}

代入w = vはload(w)と同じ効果がある.つまり,vの8個の要素を複写しwに搭載する.

例題 14..6   front(), back(), pop_back()関数

front()関数はベクタの最初の成分を返す.back()関数はベクタの最後の成分を返す.pop_back()関数はベクタの最後の成分を削除する.

#include <iostream>
#include <string>
#include <vector>
using namespace std;
typedef vector<string> Strings;
tyoedef Strings::iterator Sit;
void load(Strings&);
void print(Strings);

int main()
{
    Strings v;
    load(v);
    sort(v.begin(),v.end());
    print(v);
    cout << "v.front() = " << v.front() << endl;
    cout << "v.back() = " << v.back() << endl;
    v.pop_back();
    cout <<< "v.back() = " << v.back() << endl;
    print(v);
}

void load(Strings& v)
{
    v.push_back("Japan");
    v.push_back("America");
    v.push_back("Austria");
    v.push_back("Germany");
    v.push_back("England");
    v.push_back("Italy");
    v.push_back("Spain");
    v.push_back("Korea");
}

void print(Strings v)
{
    for(Sit it=v.begin(); it != v.end(); it++){
        cout << *it << endl;
    }
    cout << endl;
}

実行結果

\begin{figure}% latex2html id marker 4070
\centering
\includegraphics[width=7.7cm]{CPPPIC/reidai14-6.eps}
\end{figure}

erase()およびinsert()関数(erase() and insert()Functions)

例題 14..7   erase()関数の用い方

#include <iostream>
#include <string>
#include <vector>
using namespace std;
typedef vector<string> Strings;
tyoedef Strings::iterator Sit;
void load(Strings&);
void print(Strings);

int main()
{
    Strings v;
    load(v);
    sort(v.begin(),v.end());
    print(v);
    v.erase(v.begin()+2);
    v.erase(v.end()-2);
    print(v);
}

void load(Strings& v)
{
    v.push_back("Japan");
    v.push_back("America");
    v.push_back("Austria");
    v.push_back("Germany");
    v.push_back("England");
    v.push_back("Italy");
    v.push_back("Spain");
    v.push_back("Korea");
}

void print(Strings v)
{
    for(Sit it=v.begin(); it != v.end(); it++){
        cout << *it << endl;
    }
    cout << endl;
}

実行結果

\begin{figure}% latex2html id marker 4079
\centering
\includegraphics[width=7.7cm]{CPPPIC/reidai14-7.eps}
\end{figure}

v.erase(v.begin()+2)により先頭の反復子から2つ目の成分,つまり,Englandが削除された.また,v.erase(v.end()-2)により末尾の反復子を含めて2つ戻った成分,つまり,Koreaが削除された.

例題 14..8   insert()関数の用い方

このプログラムではinsert()関数の用い方とerase()関数を用いてあるセグメントを全て削除する仕方を学ぶ.

#include <iostream>
#include <string>
#include <vector>
using namespace std;
typedef vector<string> Strings;
tyoedef Strings::iterator Sit;
void load(Strings&);
void print(Strings);

int main()
{
   Strings v;
   load(v);
    sort(v.begin(),v.end());
   print(v);
    v.erase(v.begin()+2,v.end()-2);
   print(v);
    v.insert(v.begin()+2), "Swiss";
   print(v);
}

void load(Strings& v)
{
   v.push_back("Japan");
   v.push_back("America");
   v.push_back("Austria");
   v.push_back("Germany");
   v.push_back("England");
   v.push_back("Italy");
   v.push_back("Spain");
   v.push_back("Korea");
}

void print(Strings v)
{
   for(Sit it=v.begin(); it != v.end(); it++){
      cout << *it << endl;
    }
   cout << endl;
}

実行結果

\begin{figure}% latex2html id marker 4087
\centering
\includegraphics[width=7.7cm]{CPPPIC/reidai14-8.eps}
\end{figure}

find()関数(find()Function)

find()関数はベクタの成分を探すのに用いられる.

例題 14..9  

#include <iostream>
#include <string>
#include <vector>
using namespace std;
typedef vector<string> Strings;
tyoedef Strings::iterator Sit;
void load(Strings&);
void print(Strings);

int main()
{
    Strings v;
    load(v);
    print(v);
    Sit japan=find(v.begin(),v.end(),"Japan");
    Sit spain=find(v.begin(),v.end(),"Spain");
    sort(japan,spain);
    print(v);
}

void load(Strings& v)
{
    v.push_back("Japan");
    v.push_back("America");
    v.push_back("Austria");
    v.push_back("Germany");
    v.push_back("England");
    v.push_back("Italy");
    v.push_back("Spain");
    v.push_back("Korea");
}

void print(Strings v)
{
    for(Sit it=v.begin(); it != v.end(); it++){
        cout << *it << endl;
    }
    cout << endl;
}

実行結果

\begin{figure}% latex2html id marker 4095
\centering
\includegraphics[width=7.7cm]{CPPPIC/reidai14-9.eps}
\end{figure}

確認問題 14..1  

1. 配列とC++ベクタの主要な違いは何か

2. ベクタの反復子と配列の添え字はどのような点で似ているか

演習問題 14..1  

1. find()アルゴリズムを用いて次の関数を実装せよ.

int frequency(vector$<$int$>$ v, int x);

2. find()関数とerase()関数を用いて,次の関数を実装し,vectorとintについてテストせよ.

void remove_duplicates(vector$<$int$>$& v);

//vにおける繰り返しを削除

この関数は,ベクトルvにおいてxが何回発生したかを調べる.