上級プログラミング vector STL (Standard Template Library) vector

STL (Standard Template Library)
上級プログラミング
vector
vector
•
•
•
•
•
#include <vector> が必要
可変長配列 (動的にメモリが確保される)
通常の配列と同様にメモリ上に連続した領域が確保される
超便利! → 今後は積極的に使おう!!!
配列の動的確保で必要なメモリ管理をvectorまかせに出来る
<vector オブジェクトの宣言方法>
(例1) int 型の vector オブジェクト xvec を宣言
vector<int> xvec;
(例2) stringクラスのvectorオブジェクト vst を宣言
vector<string> vst;
(例3) 自作クラス myClass のvector オブジェクト mc を宣言
vector<myClass> mc;
• C++の非常に便利なクラステンプレート集
• ヘッダーファイルのincludeで即座に使える
• CでなくC++を使う理由はSTLがあるからといっ
ても過言ではない
• vector も STL に含まれる
vectorのコンストラクタ
以下では T のところに int, double, string, 自作クラス名
などが入ると思えばよい.
• vector<T>()
中身が空の T 型 vector オブジェクトを生成
• vector<T>(size_type num)
大きさが num の T 型 vector オブジェクトを生成
※ size_type は必ず0以上の値を取る整数型だと思えばよい
• vector<T>(const vector<T>& vin)
vinと全く同じ内容の T 型 vector オブジェクトを生成
(コピーコンストラクタ)
※ vector のコンストラクタは上記3つだけではない!
vectorの主要メンバ関数
以下では T のところに int, double, string, 自作クラス名
などが入ると思えばよい.
• push_back(const T& data)
dataを末尾に追加する
• size()
現在確保されている領域のサイズを返す
戻り値は size_type 型である
• clear()
現在確保されている領域を全て解放する
• operator[] (演算子のオーバーロード)
通常の配列と同様な要素アクセスを可能とする
・pop_back()
末尾のデータを削除する
サンプルコード
#include <iostream>
#include <vector>
using namespace std;
int main(){
// int型のvectorオブジェクトを宣言
vector<int> xvec;
int num;
実行例
% ./a.out
3 5 -4 0 1 -7 9 15
1st: xvec.size() =
3 5 -4 0 1 -7 9 15
2nd: xvec.size() =
3rd: xvec.size() =
%
// データを繰り返し入力
while(cin >> num){
xvec.push_back(num); // push_back を使ってデータを順次追加
}
// データ数をとりあえず見てみる.
cout << "1st: xvec.size() = " << xvec.size() << endl;
for(unsigned int i = 0; i < xvec.size(); ++i){
cout << xvec[i] << " "; // operator[]によって配列同様の要素アクセス
}
cout << endl;
xvec.pop_back(); // 末尾のデータを削除
// 削除後にデータ数がどうなっているか確認
cout << "2nd: xvec.size() = " << xvec.size() << endl;
xvec.clear(); // 全データ削除
cout << "3rd: xvec.size() = " << xvec.size() << endl;
return 0;
}
vector オブジェクトと関数の関係
•
vectorオブジェクトは関数の引数に出来る
(重要事項)
関数の中でvectorオブジェクトが変更されないとしても
積極的に参照引数を使おう!
(理由) 参照引数を使うことでメモリ効率が向上するため
(例1) 関数の中で xvec が変更されないとき
void func(const vector<double>& xvec);
// vector<double> xvec でも問題なく動作するがメモリを無駄に消費する
(例2) 関数の中で xvec が変更されるとき(これまで通りの使用法)
void func(vector<double>& xvec);
•
vectorオブジェクトは関数の戻り値の型として使える
(vectorオブジェクトを返せる)
(例3) 関数の中で生成したvectorオブジェクトを返す場合
vector<double> func(const int num){ // 引数は何でもOK!
vector<double> avec;
//(avecを使った処理)
return avec;
}
#include <iostream>
#include <vector>
using namespace std;
サンプルコード
// データ入力のための関数(xvecは変更される)
void input(vector<int>& xvec){
for(int i = 0; i < 6; ++i){
xvec.push_back(i * i);
}
}
// データ出力のための関数(xvecは変更されない)
void output(const vector<int>& xvec){
for(unsigned int i = 0; i < xvec.size(); ++i){
cout << xvec[i] << " ";
実行例
}
% ./a.out
cout << endl;
0 1 4 9 16 25
}
%
int main(){
vector<int> xvec;
input(xvec); // vectorオブジェクト名をそのまま書く
output(xvec); // こちらも input と同様にそのまま書く
return 0;
}
2
9
2
8
0
vectorと配列との関係
• 関数の引数として配列の先頭アドレスなどが要求される場合も
vectorを使用可能!
(理由1) vector<T>オブジェクトの中身は,
通常の T 型変数で構成されているため
(理由2) 通常の配列と同様に,メモリ上で連続した
領域が確保されることが保証されているため
(例1) 関数の引数が double* 型を要求している場合
void func(double* avec); // 関数宣言
// vector<double> bvec を使う場合(main関数等における呼び出し)
func(&bvec[0]); // bvec[0]はvectorの中身の先頭を意味するため,
// &bvec[0]は配列の先頭アドレスとなる
配列 vs. vector
• どちらを使っても同様の処理は可能
• new と [ ] を使って配列を動的に確保する場合,
メモリ管理を作成者が行う必要あり
→ 必ず delete [ ] によって解放するのは意外に大変
→ vector にメモリ管理を任せれば,
自分がメモリ管理することから解放される!
※ 単にbvecと書いた場合,配列の先頭アドレスにはならない
2次元配列のようなvectorオブジェクト
• vector<T> の T に,再度 vector<T> を入れることで,2
次元配列のようなvector オブジェクトを生成可能
(例) double 型の場合
vector<vector<double> > aMat;
ここの空白は必ず必要!
#include <iostream>
#include <vector>
using namespace std;
サンプルコード
実行例
% ./a.out
Input 1-th row --> 5
Input 2-th row --> 2 7
int main(){
Input 3-th row --> 1 0 4
vector<vector<double> > aMat;
Input 4-th row --> 9 2 8 4
vector<double> aRow; // 行の入力用
Input 5-th row --> 7 3 0 9 1
for(int i = 0; i < 5; ++i){
5
cout << "Input “ << i + 1
2 7
<< "-th row --> ";
1 0 4
for(int j = 0; j < i + 1; ++j){
9 2 8 4
int num;
7 3 0 9 1
cin >> num; // データの入力
aRow.push_back(num); // データを順次追加 %
}
aMat.push_back(aRow); // aMatの要素は vector<double> なのでaRowを入れる
aRow.clear(); // 次の行のデータを入れるためにclearしておく
}
// 入力された結果の表示
for(unsigned int i = 0; i < aMat.size(); ++i){
for(unsigned int j = 0; j < aMat[i].size(); ++j){
cout << aMat[i][j] << " ";
}
cout << endl;
}
return 0;
}
algorithm ヘッダーに含まれる
sortを使ってvectorオブジェクトをソート
•
vector<int>, vector<double>, vector<char> などの
基本型のvector オブジェクトをソート
1. sort(xvec.begin(), xvec.end());
※ begin() と end() は「イテレータ」に関係するものである
(イテレータに関して,ここでは深入りしないことにする)
• vector<myClass> のように自作クラスのvector オブジェクトをソート
1. myClass において,
bool operator<(const myClass&) const;
を適切に定義する(重要事項:最後のconstは必ず必要!)
→ myClass において何を基準に大小関係を決定するかを定義する
2. sort(xvec.begin(), xvec.end());
※ sortを使うためには「#include <algorithm>」を書く必要有り
自作クラスのオブジェクトを
vectorオブジェクトにpush_back
• 先に自作クラスのオブジェクトを作ってから
push_backする方法
(例) myClass temp(’A’, 3);
vector<myClass> obj;
obj.push_back(temp);
• コンストラクタを利用してpush_backする方法
(例) vector<myClass> obj;
obj.push_back( myClass(’A’, 3) );