■STLメモ
テンプレート
コンパイル時に型が決まるらしい。いろんな型を入れるサンプル。
#include <iostream>
using namespace std;
template <class MyTemplate>
MyTemplate TGetMax( MyTemplate &a, MyTemplate &b, MyTemplate &c ){
MyTemplate &x = a >b? a:b;
MyTemplate &y = b >c? b:c;
return x > y? x:y;
}
int main(void){
int na=100, nb=200, nc=300;
char cha='A', chb='B', chc='C';
double da=1.1, db=1.2, dc=1.3;
cout << TGetMax( na, nb, nc ) << endl;
cout << TGetMax( cha, chb, chc ) << endl;
cout << TGetMax( da, db, dc ) << endl;
return 0;
}
うまく使うと便利かも。なお、MyTemplateはよくTとして使われる。
Cの配列にSTLを使用
Cでの配列にSTLを使う例。
#include <iostream>
#include <algorithm>
using namespace std;
template <class T>
class MyTemplateArray{
public:
T array[5];
void SetValue(int item, T val);
T &GetValue(int item);
};
template <class T>
void MyTemplateArray<T>::SetValue(int item, T val){
if( item > 4 || item < 0 ){
cout << "Error: Item is out of range!!" << endl;
return;
}
array[item] = val;
}
template <class T>
T &MyTemplateArray<T>::GetValue(int item){
if( item > 4 || item < 0 ){
cout << "Error: Item is out of range!!" << endl;
return array[0];
}
return array[item];
}
int main(){
MyTemplateArray<int> mt;
// set array
for(int i=0; i<5; i++){ mt.SetValue( i, rand() ); }
// print
for(int i=0; i<5; i++){ cout << mt.GetValue(i) << ' '; } cout << endl;
return 0;
}
しかし、このような使い方は一般的ではない。
vectorによる2次元配列
vectorで2次元配列を作る。1つ目は直に値を入れる方法、2つ目はvectorをプッシュする方法。
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
int main( void ){
// array1
cout << "===========array1\n";
vector< vector<int> > array;
// resize
int size=4;
array.resize( size );
for(int i = 0; i<size; i++){ array[i].resize( size ); }
// set
for(int i=0; i< size;i++){
for(int j=0; j<size; j++){
array[i][j] = (i+1)*(j+1);
}
}
// print
for(int i=0; i< array.size();i++){
for(int j=0; j<array[i].size(); j++){ cout << array[i][j] << " "; }
cout << endl;
}
// array2
cout << "===========array2\n";
vector< vector<int> > array2;
vector<int> temp;
// set
for(int i=1; i<5; i++){
for(int j=1; j<5; j++){
temp.push_back( i * j );
}
array2.push_back( temp );
temp.erase( temp.begin(), temp.end() );
}
// print
for(int i=0; i< array2.size();i++){
for(int j=0; j<array2[i].size(); j++){ cout << array2[i][j] << " "; }
cout << endl;
}
cout << "===========\n";
// push
array[0].push_back(5);
// print
cout << endl << "push 5" << endl;
for(int i=0; i< array.size();i++){
for(int j=0; j<array[i].size(); j++){ cout << array[i][j] << " "; }
cout << endl;
}
return 0;
}
関数オブジェクト
- 関数オブジェクト
- 関数のように呼び出すことのできるオブジェクト。operator()()演算子関数を実装したクラスオブジェクト。
らしい。関数オブジェクトを使った例。
#include <iostream>
include <vector>
#include <functional>
#include <algorithm>
using namespace std;
template <class T>
struct ShowData : public unary_function<T, void>{
void operator()(T n){
cout << n << ' ';
}
};
int main( void ){
vector<int> array;
// set
for(int i=0; i<5; i++){
array.push_back(i);
}
// show data
for_each(array.begin(), array.end(), ShowData<int>() );
cout << endl;
return 0;
}
おぉ、これは便利。
動的に2次元配列
#include <iostream>
using namespace std;
int main(void){
int N=10;
double** a = new double*[N];
for(int i=0;i <N; i++){
a[i] = new double[N];
}
a[0][1] = 1.0;
printf("%lf\n", a[0][1] );
// delete
for(int i=0;i <N; i++){
delete[] a[i];
}
delete[] a;
return 0;
}
こうしないと、環境によってはコンパイルエラー。
もう少し改良したもの。
#include <iostream>
using namespace std;
int main(void){
int col=3;
int row=2;
int** a = new int*[row];
for(int i=0;i <row; i++){
a[i] = new int[col];
}
a[0][0] = 0;
a[0][1] = 1;
a[0][2] = 2;
a[1][0] = 3;
a[1][1] = 4;
a[1][2] = 5;
for(int i=0; i<row; i++){
for(int j=0; j<col; j++){
printf("%d ", a[i][j] );
}
printf("\n");
}
// delete
for(int i=0;i <row; i++){
delete[] a[i];
}
delete[] a;
return 0;
}
要素をコピー
vectorのdataからtempにコピーするには、次のように行う。
tempを定義する際に、サイズを書き忘れるとbus errorというエラーになる。
vector<int> temp( data.size() );
copy( data.begin(), data.end(), temp.begin() );
要素の重複を除く
vector<int>のlistの重複を除いたuniq_dataというのを作る。上のコピーを使う。
vector<int> uniq_data( list.size() );
copy( list.begin(), list.end(), uniq_data.begin() );
sort( uniq_data.begin(), uniq_data.end() );
vector<int>::iterator ite = unique( uniq_data.begin(), uniq_data.end() );
uniq_data.erase( ite, uniq_data.end() );
これで重複しないものができる。
コンマで分割
コンマ区切りのテキストをint型のvectorに入れて返す関数。perlのsplitみたいなもの。
vector<int> splitStringWithComma( string& input ){
int num = 0;
string str = "";
vector<int> list;
for(int i=0; i<(int)input.size(); i++){
//cout << input[i] << endl;
if( input[i] == ',' ){
//cout << "str= " << str << endl;
istringstream iss( str );
iss >> num;
list.push_back( num );
str = "";
continue;
}
str += input[i];
}
/* last number */
istringstream iss( str ); iss >> num;
list.push_back( num );
return list;
}
最後の一つを入れるのにもうちょっときれいな書き方はないかなぁ~。
クラスの分割ファイル
STLとは関係ないけど、テンプレとして。
node.h
#ifndef _INC_NODE_
#define _INC_NODE_
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Node {
private:
int ID;
string name;
public:
/* constructor */
Node();
~Node();
/* set */
void setID( int );
void setName( string );
/* get */
int getID();
string getName();
};
#endif
node.cpp
#include <iostream>
#include <vector>
#include <string>
#include "node.h"
using namespace std;
/* constructor */
Node::Node(){ ID = -1; name = ""; }
/* destructor */
Node::~Node(){ }
/* set */
void Node::setID(int i){
if( i >= 0 ){ ID = i; }
else { cout << "set ID error" << endl; }
}
void Node::setName(string str){
if( str != "" ){ name = str; }
else { cout << "set Name error" << endl; }
}
/* get */
int Node::getID(){ return ID; }
string Node::getName(){ return name; }
nodetest.cpp
#include <iostream>
#include <vector>
#include <string>
#include "node.h"
using namespace std;
int main(void){
Node node1;
node1.setID(0);
node1.setName("root");
cout << node1.getID() << " " << node1.getName() << endl;
return 0;
}
要素の挿入
vector<int>に要素を挿入する。よくあるサンプルは以下のようなもの。
vector<int> list;
list.push_back(1);
list.push_back(2);
list.push_back(4);
vector<int>::iterator ite = list.begin();
list.insert( ite+2, 3 ); /* 0から数えて、2番目の前(=4)に挿入 */
この方法で、連続的にデータを挿入するときには、少し注意が必要。
それは、毎回iteratorをセットすること。例えば、
for(int i=0; i<(int)a.size(); i++){
ite = b.begin();
if( a[i] == -1 ){ b.insert( ite+i, -1 ); }
}
というようにする。
要素の削除
リストから-1をすべて削除する例。
int pos = 0;
vector<int>::iterator ite = list.begin();
for(int i=0; i<(int)list.size(); i++){
if( list[i] == -1 ){
pos = i;
ite = list.begin();
for(int j=0; j<pos; j++){
ite++;
}
list.erase( ite );
i = pos-1;
}
}
最大・最小を求める
最大・最小を求めるときに便利な*max_element()。しかし、要素が0のものに
対して行うとbus errorになるので注意。
if( (int)list.size() != 0 ){
int max = *max_element( list.begin(), list.end() );
}
とする。
vectorへのアクセス
当たり前だが、空のvectorにアクセスすると、セグメンテーションのエラー。
普通はforループでアクセスするため、(int)list.size()のような形で個数をチェックしている。
しかし、直接n番目の要素にアクセスしようとするときに注意が必要。特に、二次元配列のn行目にアクセスするとき。
こうするのがいいのかな?
int target_line = 10;
if( (int)array.size() != 0 ){
for(int j=0; j<(int)array[target_line].size(); j++){
cout << array[target_line][j] << " ";
}
} else {
cout << "array is empty!!" << endl;
}
参考文献:柏原正三「標準C++ STLの基礎知識」、アスキー2001
最終更新:2007年02月01日 18:40