vectorとイテレータ

#include <iostream>
#include <vector>
#include <algorithm>
 
int main()
{
	using std::cout;
	using std::endl;
	//普通の配列っぽく宣言
	std::vector<int> arr = { 1,2,3 };
	for (int i = 0; i < arr.size(); i++) {
		cout << arr[i] << ",";
	}
	cout << endl;
 
	//要素の追加
	cout << "size:" << arr.size() << endl;
	for (auto i = 0; i < 10; i++)
	{
		arr.push_back(i);
	}
	cout << "size:" << arr.size() << endl;
 
	//範囲ループ
	for (auto x : arr) {
		cout << x << ",";
	}
	cout << endl;
 
	//要素の挿入 index=2の位置に100を挿入
	arr.insert(arr.begin() + 2, 100);
	//arr.begin() => arr[0] 
	//arr.begin()+2 => arr[0+2] => arr[2]
		//範囲ループ
	for (auto x : arr) {
		cout << x << ",";
	}
	cout << endl;
	//要素の削除 index=2の位置の要素を削除
	arr.erase(arr.begin() + 2);
	for (auto x : arr) {
		cout << x << ",";
	}
	cout << endl;
 
	//イテレータ(繰り返しのためのポインタ)
	// std::vector<int>::iterator theI;
	//for (std::vector<int>::iterator theI = arr.begin();
	cout << "イテレータのサンプル" << endl;
	for(auto theI = arr.begin();
		theI != arr.end(); theI++)
	{
		cout << (*theI) << ",";
	}
	cout << endl;
 
	cout << "逆イテレータのサンプル" << endl;
	for (auto theI = arr.rbegin();
		theI != arr.rend(); theI++)
	{
		cout << (*theI) << ",";
	}
	cout << endl;
 
	cout << "std::sort のサンプル" << endl;
	std::sort(arr.begin(), arr.end());
	for (auto theI = arr.begin();
		theI != arr.end(); theI++)
	{
		cout << (*theI) << ",";
	}
	cout << endl;
 
	cout << "std::swap のサンプル" << endl;
	std::swap(arr[0], arr[2]);
	for (auto theI = arr.begin();
		theI != arr.end(); theI++)
	{
		cout << (*theI) << ",";
	}
 
	arr.clear();
	cout << "size:" << arr.size() << endl;
	return 0;
}

イテレータとstlコンテナ

STLでデータを入れるための箱を、コンテナと呼ぶのは覚えてる?
vectorや、list、mapはデータを入れるためのコンテナということになる。
その他それらを扱うための関数群や、便利に使うためのクラス群をまとめてSTLと呼んでいる。
コンテナを使うために必須になってくるのが、イテレータである。
イテレータは、繰り返し処理を便利に行うための仕組みで、野生のC++状態で言うとポインタの代わりになるものである。

機能としては、

という、実際ポインタと同じ役割を果たす。

Listing. 1: 野生のC++のポインタ
"main.cpp"
#include <iostream>
 
int main() {
	int array[10]{1,2,3,4,5,6,7,8,9,10};
	int* ptr = array;
	std::cout << *ptr << std::endl;
	std::cout << *(ptr + 1) << std::endl;
	std::cout << *(ptr + 2) << std::endl;
}

野生のC++では、配列の要素にポインタ経由でアクセスするのにこのような形でアクセスするのが定番。
これはもう慣れたもんかな?
ポインタは、配列の型によって、+1すると配列の要素1個のメモリ分飛んでくれるので、どんな型、クラス、構造体の配列に対しても+1するとその要素1個分ずれてくれるありがたいお方だった。

Listing. 2: 野生のC++の配列へのポインタによるアクセス(繰り返し)
"main.cpp"
#include <iostream>
 
class poo
{
public:
	poo(int _a, int _b):a(_a),b(_b){}
	int a;
	int b;
};
const int arrNum = 5;
int main() {
	poo array[arrNum]{
		{1,1},{2,2},{3,3},{4,4},{5,5}
	};
	poo* ptr = array;
	for(int i=0; i<arrNum; i++){
		std::cout << ptr->a << ", " << ptr->b << std::endl;
		ptr++;
	}
}

この様にポインタをインクリメント、デクリメントして、繰り返しの中で便利に使っていくと、配列へのアクセスがとても便利に行えるんだったね。
しかしこの方法では、配列の初めの要素(=配列名)、配列のサイズ(=arrNum)がわかっていないと、領域オーバーしちゃうことがある。
などなどの様々な問題をはらんでいる。

STLコンテナのイテレータは、ポインタと同じように使えて、なおかつ安全性を確保しつつ、コンテナの特性に合わせた便利なアクセス方法を提供する。
(すてきやん)

Listing. 3: std::arrayとイテレータの取得
"theMain.cpp"
#include <iostream>
#include <array>
#include <ostream>
 
class poo
{
public:
	poo(int _a, int _b):a(_a),b(_b){}
	int a;
	int b;
	//<<演算子をオーバーロードしてフレンド登録(習ってないから気にするな!)
	//これをやるとcoutに自分のクラスを渡したときの挙動を書ける(かっこいい)
	friend std::ostream& operator<<(std::ostream& os, const poo& _dat);
	friend std::ostream& operator<<(std::ostream& os, const poo* _dat);
};
//coutにpooの参照を渡したときの挙動を書く
std::ostream& operator<<(std::ostream& os, const poo& _dat)
{
	os << _dat.a << ", " << _dat.b;
	return os;
}
//coutにpooのポインタを渡したときの挙動を書く
std::ostream& operator<<(std::ostream& os, const poo* _dat)
{
	os << _dat->a << ", " << _dat->b;
	return os;
}
 
const int arrNum = 5;
 
int main() {
	std::array<poo, arrNum> arr = {
	{{1,1}, {2,2}, {3,3}, {4,4}, {5,5}}
	};
	std::array<poo, arrNum>::iterator theI;//イテレータを宣言
	theI = arr.begin();//begin()は配列の先頭を指すポインタを返す
	std::cout << theI << std::endl;
	theI = arr.end();//end()は配列の最後の要素の次にある要素のポインタを返す
	std::cout << theI << std::endl;
	theI = arr.end() -1;//end()-1は配列の最後の要素のポインタを返す
	std::cout << theI << std::endl;
}

イテレータの型は、コンテナの型::iteratorとなる。
(コンテナの型=コンテナ+型パラメータだよ。例)array<int, 10>とか vector<double>とか)

上のソースの様にコンテナのインスタンスから、イテレータを取得できる。

や、逆イテレータなど、コンテナによって、いろいろある。

あとは、

コンテナの種類によっては使えないイテレータ操作とかがあるよ。
(例えば、単方向リストでは++できるけど、--はできない。単方向リストだからね。)

あと、いちいち、コンテナ+型パラメータ::iteratorとか書いてられないので、autoやauto&を使って、推論してもう事ができる(なれたらこっちを使おう)

"イテレータの型推論"
	std::array<poo, arrNum>::iterator theI;//イテレータを取得
	theI = arr.begin();//begin()は配列の先頭を指すポインタを返す
	//ここまでをauto推論を使って
	auto itr = arr.begin();
	//と書くことができる

なので、上のarrayのfor文表示に関しては以下の様にかける。

"theMain"
    #include <iostream>
#include <array>
#include <ostream>
 
class poo
{
public:
	poo(int _a, int _b):a(_a),b(_b){}
	int a;
	int b;
	//<<演算子をオーバーロードしてフレンド登録(習ってないから気にするな!)
	//これをやるとcoutに自分のクラスを渡したときの挙動を書ける(かっこいい)
	friend std::ostream& operator<<(std::ostream& os, const poo& _dat);
	friend std::ostream& operator<<(std::ostream& os, const poo* _dat);
};
//coutにpooの参照を渡したときの挙動を書く
std::ostream& operator<<(std::ostream& os, const poo& _dat)
{
	os << _dat.a << ", " << _dat.b;
	return os;
}
//coutにpooのポインタを渡したときの挙動を書く
std::ostream& operator<<(std::ostream& os, const poo* _dat)
{
	os << _dat->a << ", " << _dat->b;
	return os;
}
 
const int arrNum = 5;
 
int main() {
	std::array<poo, arrNum> arr = {
	{{1,1}, {2,2}, {3,3}, {4,4}, {5,5}}
	};
 
	for(auto itr=arr.begin(); itr != arr.end(); itr++){
		std::cout << itr << std::endl;
	}
}

お題

以下ヒント(ほとんど答え)

#include <iostream>
#include <array>
#include <ostream>
#include <map>
#include <string>
 
class poo
{
public:
	poo(int _a, int _b) :a(_a), b(_b) {}
	int a;
	int b;
	//<<演算子をオーバーロードしてフレンド登録(習ってないから気にするな!)
	//これをやるとcoutに自分のクラスを渡したときの挙動を書ける(かっこいい)
	friend std::ostream& operator<<(std::ostream& os, const poo& _dat);
	friend std::ostream& operator<<(std::ostream& os, const poo* _dat);
};
//coutにpooの参照を渡したときの挙動を書く
std::ostream& operator<<(std::ostream& os, const poo& _dat)
{
	os << _dat.a << ", " << _dat.b;
	return os;
}
//coutにpooのポインタを渡したときの挙動を書く
std::ostream& operator<<(std::ostream& os, const poo* _dat)
{
	os << _dat->a << ", " << _dat->b;
	return os;
}
 
int main() {
	std::map<std::string, poo> arr = {
		{//"キー", {poo型のデータ}
		{"a",{1,1}},
		{"b",{2,2}},
		{"c",{3,3}},
		{"d",{4,4}},
		{"e",{5,5}}
		}
	};
	//arr[a]には、poo型で{1,1}
	//arr[e]には、poo型で{5,5}が入っている
	//mapの中身を出力:mapはキーとデータを持つ、キーは->firstで、データは->secondでアクセスできる
	//itrは、map<string,poo>なので、itr->firstとitr->secondでstringとpooのデータにアクセスできる
	for (auto itr = arr.begin(); itr != arr.end(); itr++) {
		std::cout << frist(キーにアクセス) << ": " << second(データにアクセス) << std::endl;
	}
}