参考:http://d.hatena.ne.jp/faith_and_brave/20100201/1264997004
「C++0xになると、C++03 でごちゃごちゃした部分がだいぶすっきり書けるようになる」
らしいですが、C++0xを待たなくてもBoostを使えばだいぶすっきり書けるので、
BoostでのC++入門はこんな感じだよー、という気持ちで以下略。
この記事はC言語をある程度理解していることが前提です。
C++/Boostでの出力はC++標準の IOStream ライブラリと Boost.Format を組み合わせて行います。
例として、C言語のprintfを用いたHello World を、C++/Boostを使って書き直してみます。
#include<stdio.h>int main(){ printf("%s\n","Hello, World" );return0;}
以上のコードをC++/Boostで書くと、以下のようになります。
#include<iostream>// std::cout を使うのに必要#include<boost/format.hpp>// boost::format を使うのに必要int main(){ std::cout << boost::format("%s\n") %"Hello, World";return0;}
std::cout というのは標準出力です。Cで言う stdout です。
「標準出力(consoleout)に "Hello, World" を整形(format)して出力する」と読みます。
boost::format に渡す文字列は、C言語の printf と同じスタイルで書くことが出来ます。
複数の引数を渡すには、
std::cout << boost::format("%s,%s\n") %"Hello" %"World";
のように %演算子をつなげて書きます。
またそれとは別のスタイルとして、
std::cout << boost::format("%1%, %2%\n") %"Hello" %"World";
このように、何番目の引数を表示させるかを明記して書くことも出来ます。
その場合、
std::cout << boost::format("1, 2, %1%, 4, %2%, %1%, 7, 8, %1%, %2%\n") %"Fizz" %"Buzz";
このように引数を何回も使い回したりできます。
使い回さない場合でも、この書き方なら いちいち型を指定する必要がないのでオススメです。
Boost.Format に渡す値は、別に文字列である必要はありません。
#include<iostream>#include<boost/format.hpp>int main(){ std::cout << boost::format("%1% = %2%\n") %"1 + 1" % (1 +1 );return0;}
このように書くことで、式の計算結果を表示させることも出来ます。
()でくるんであるのは %演算子の優先順位の関係です。 Boost.Format に渡す引数が複雑な場合は()でくるんで渡した方が悩まないで済むと思います。
C言語と同様、整数同士の割り算を行う場合には注意が必要です。
#include<iostream>#include<boost/format.hpp>int main(){ std::cout << boost::format("%1%\n") % (3 /2 );// 端数が切捨てされる std::cout << boost::format("%1%\n") % (3.0 /2.0 );// きちんと端数も考慮されるreturn0;}
複雑な計算を行わせることも出来ます。
#include<iostream>#include<boost/format.hpp>#include<cmath>// std::sin を使うのに必要int main(){ std::cout << boost::format("%1%\n") % (1 *2 +2 *3 );// * は + より優先される std::cout << boost::format("%1%\n") % (10 * (10 -1 ) /2 );// () を用いて優先順位を変えられる std::cout << boost::format("%1%\n") % std::sin(1.0);// sinを求める。単位はラジアンreturn0;}
C言語と同様ですが、C++/Boostでは基本的にconst を付けて宣言することが望ましいです。
#include<string>// std::string を使うのに必要#include<iostream>#include<boost/format.hpp>int main(){constint i =23;// 3 という整数(int)に a という名前を割り当てるconst std::string s ="abc";// こちらは文字列 std::cout << boost::format("i の値は'%1%', s の値は'%2%'.\n") % i % s;return0;}
このように "const 型 変数名= 値;" のように書ことで、その値に対して名前をつけることが可能になります。
以降は、23といった具体的な値の代わりに、i といった宣言した変数の名前を書くことで、具体的な値を書いたのと同じように振舞わせることが出来ます。
"const 型 変数1= 値1, 変数2= 値2, 変数3= 値3;" のように、複数の変数を定義することもできます。
#include<iostream>#include<boost/format.hpp>int main(){constint mikan =30, apple =100;// みかんとりんごの値段 std::cout << boost::format("みかん7個とりんご3個で、合計 %1% 円\n") % ( mikan *7 + ringo *3 );return0;}
関数を使うことで処理に名前を付けることが出来ます。
"戻り値の型 関数名( 仮引数リスト){ 処理}" のように書きます。
// 引数を取らない例int the_Answer_to_life_the_universe_and_everything(){// "return 値;" で値を返すreturn42;}// 引数ひとつを取る例int square(int x ){return x * x;}// 複数の引数を取る例double ave2(double x,double y ){return ( x + y ) /2;}
関数を呼び出すには、 "関数名( 引数リスト)" のように書きます。
// さっきの関数定義がここにあるとする#include<iostream>#include<boost/format.hpp>int main(){// 関数の戻り値に名前をつけるconstint i = the_Answer_to_life_the_universe_and_everything();constint s = square(i); std::cout << boost::format("%1% の二乗は %2% です。\n") % i % s;// 直接使うことも出来る std::cout << boost::format("%1% と %2% の平均は %3% です。") % i % s % ave2( i, s );return0;}
C言語と同様、何も値を返さない関数を定義することも出来ます。その場合はvoid という特別な型を使います。
#include<iostream>#include<boost/format.hpp>void disp(const std::string& s){ std::cout << boost::format("%1%\n") % s;}int main(){ disp("Hello World");return0;}
またBoostには関数をラップした Boost.Function というものも存在し、これを使うことで、関数を変数として扱うことが出来ます。
#include<iostream>#include<boost/format.hpp>#include<cmath>// for std::sqrt#include<boost/function.hpp>int the_Answer_to_life_the_universe_and_everything(){return42;}void disp(const std::string& s){ std::cout << boost::format("%1%\n") % s;}double norm2(double x,double y ){return std::sqrt( x * x + y * y );}int main(){// the_Answer_to_life_the_universe_and_everything に別名を付けるconst boost::function<int()> f1 = the_Answer_to_life_the_universe_and_everything; std::cout << boost::format("%1%\n") % f1();// f1() は the_An(中略)ing() と同じ// disp に別名を付けるconst boost::function<void(const std::string&)> f2 = disp; f2("Hello World");// disp("Hello, World"); と同じ// norm2 に別名を付けるconst boost::function<double (double,double)> f3 = norm2; std::cout << boost::format("%1%\n") % f3(3,4 );// norm2( 3, 4 ) と同じreturn0;}
型名は"[]boost::function<[] 戻り値の型( 引数型リスト)[]>[]"となります。
C++/Boost には以下のような型が用意されています(一部です)。
基本データ型int : 整数型(0,1,2,3, -1, etc...)char : 文字型('a','b','c','1', etc...)double : 浮動小数点数型(0.1,3.14, etc...)bool : 論理型(true,false)複合データ型boost::array<T, N> : 配列(<boost/array.hpp>をインクルード)std::vector<T> : リスト(<vector>をインクルード)std::string : 文字列(<string>をインクルード)std::map<Key, Value> : 辞書(<map>をインクルード)std::set<T> : 集合(<set>をインクルード)boost::function<Signature> : 関数(<boost/function.hpp>をインクルード)boost::tuple<T1,T2, ...> : 複数の値の詰め合わせ(<boost/tuple/tuple.hpp>をインクルード)boost::shared_ptr<T> : ポインタ(<boost/shared_ptr.hpp>をインクルード)boost::optional<T> : T型か無効値(<boost/optional.hpp>をインクルード)boost::variant<T1,T2, ...> : 可変型(Cでいうunion)(<boost/variant.hpp>をインクルード)boost::any : 任意型(<boost/any.hpp>をインクルード)
C++/Boost の演算子は殆どC言語と同様です。
ただし>>演算子および<<演算子は、従来の意味(ビットシフト)に加えて「ストリーム入出力」という意味を持ちます。
また %演算子も、従来の意味(剰余演算)に加えて「フォーマット出力」という意味を持ちます。
これらは「演算子多重定義」という仕組みによって実現されていますが、ライブラリ製作者以外はその仕組を学習する必要はないでしょう。
参照は変数に別名を与えます。
// const参照constint a =42;constint& b = a;// b は a を参照するstd::cout << boost::format("%1%\n") % b;// boost::format("%1%\n") % a と同じ// 可変な参照int x =1;int& y = x;// y は x を参照するy =23;// x = 23; と同じ。参照先の x を書き換える
参照は関数の引数として使うことが出来ます。
特にconstな参照で渡された変数は、無駄なコピー動作を起こさないので、コストを気にする場合にしばしば用いられます。
また、可変な参照を、複数の値を返すために使うことも出来ます。
// http://d.hatena.ne.jp/faith_and_brave/20100201/1264997004 の例void get_ip(int& aa,int& bb,int& cc,int& dd){ aa =127; bb =0; cc =0; dd =1;}int a =0;int b =0;int c =0;int d =0;get_ip(a, b, c, d);
しかし、その場合はconstではなくなってしまう為、 Boost.Tuple を使った方が良いです:
#include<boost/tuple/tuple.hpp>boost::tuple<int,int,int,int> get_ip(){return boost::make_tuple(127,0,0,1 );}#include<iostream>#include<boost/format.hpp>int main(){const boost::tuple<int,int,int,int> t = get_ip();constint& a = boost::get<0>(t);constint& b = boost::get<1>(t);constint& c = boost::get<2>(t);constint& d = boost::get<3>(t); std::cout << boost::format("%1%.%2%.%3%.%4%\n") % a % b % c % d;}
C++/Boost では、C言語の if/switch/while/for に加え、範囲 for 構文を擬似的にサポートしています。
#include<boost/foreach.hpp>// マクロBOOST_FOREACHを使えるようにする#include<iostream>#include<boost/format.hpp>int main(){constint a[] = {1,2,3}; BOOST_FOREACH(constint x, a ) { std::cout << boost::format("%1%\n") % x; }return0;}
また、 if 文の条件部や for 文の初期化部等で変数を宣言することも出来ます。
#include<cmath>#include<boost/optional.hpp>#include<iostream>#include<boost/format.hpp>// boost::optional を使った例// 詳しく説明すると脱線するので「こんな書き方もある」程度に思って下さいboost::optional<double> my_sqrt(double x ){if( x <0 ) {return boost::none; }else {return std::sqrt(x); }}int main(){// if文中で変数を宣言しif(const boost::optional<double> x_ = my_sqrt(-1) ) {constdouble& x = x_.get();// それを使う std::cout << boost::format("%1%\n") % x; }else { std::cout << boost::format("%1%\n") %"負の値です!"; }return0;}
Cで言う構造体です。C++/Boost では、クラスを使うことで新しい型を定義することが出来ます。
今まで出てきた std::string や boost::function などは全てクラスです。これらは基本型と殆ど同じように扱うことが出来る他、 "変数名.メンバ関数名" と書くことにより、そのクラス専用に用意された関数を使うことが出来ます。
const std::string s ="abc";std::cout << boost::format("%1%\n") % s.length();// s の長さを求める
ユーザ側で定義することも出来ますが、ライブラリ側で定義されたクラスを使うだけでも、十二分にプログラミングは可能です。
自分でクラスを作ることは落とし穴が多いので、慣れないうちはライブラリ定義のクラスを使いましょう。
boost::function<int()> 等で既に触れられている物です。
任意の型に対して同じことをさせたい場合に用います。
例として簡単なテンプレート関数を作ってみると、
template<typename T>void disp( T x ){ std::cout << boost::format("%1%\n") % x;}
このように書くことで、disp 関数は、あらゆる型の変数 x を受け入れることが出来るようになります。
例えば、
disp<int>(23);
のように呼び出すと、disp のT をint で置き換えた
void disp(int x ){ std::cout << boost::format("%1%\n") % x;}
という関数がコンパイラによって自動生成され、呼び出されます。
同様に[]disp<std::string>("abc");[] のように呼び出すと、
void disp( std::string x ){ std::cout << boost::format("%1%\n") % x;}
という関数が呼び出されます。
また、これらの角括弧 <> は省略可能です。
constdouble d =3.14;disp(d);
このように書けば、d の型であるdouble 版のdisp が呼ばれます。
また、前項のクラスも、テンプレートにすることが出来ます。[]std::vector<int> v;[] のように使います。
その場合、角括弧は省略不能です。
C言語の配列はC++/Boostでも使えますが、C++/Boostにはは様々な点で便利なboost::array<T, N> というクラスが用意されています。
このクラスは通常の配列と同様に扱える他、メンバごとのコピーなどを簡単に行えます。
#include<boost/array.hpp>#include<boost/foreach.hpp>#include<iostream>#include<boost/format.hpp>int main(){const boost::array<int,3> a = {{1,2,3 }};// 要素数取得constint size = a.size();// 3// 要素アクセスconstint front = a.front();// 1constint back = a.back();// 3constint second = a[1];// 2// 全部表示 BOOST_FOREACH(constint x, a ) { std::cout << boost::format("%1%\n") % x; }// コピー boost::array<int,3> b = a;// 破壊的動作// 要素書き換えfor( std::size_t i =0; i < b.size(); ++i ) { b[i] = i; }// 全要素を 0 で埋める b.assign(0 );return0;}
C言語では取り扱いの難しかった可変長配列は、C++/Boost ではstd::vector<T> というクラスにまとめられています。
#include<vector>#include<boost/assign.hpp>#include<boost/foreach.hpp>#include<iostream>#include<boost/format.hpp>int main(){// 構築は少し面倒。C++の次世代規格で改善されるconst std::vector<int> a = boost::assign::list_of(1)(2)(3)(4);// boost::array と同じように扱えるconstint size = a.size();// 4constint front = a.front();// 1constint back = a.back();// 4constint second = a[1];// 2// 以下、破壊的動作 std::vector<int> b;using boost::assign::operator+=;// += による要素追加を行うのに必要 b +=2,3,5,7,11,13;// 連続的要素追加 b.push_back(17 );// 後ろに要素を追加する b.pop_back();// 最後の要素を削除する// 要素書き換え// BOOST_FOREACH を使ってみる BOOST_FOREACH(int& x, b )// int& とすることで、書き換えが可能になる { x *=2; }// 全部表示 BOOST_FOREACH(constint x, b ) { std::cout << boost::format("%1%\n") % x; }return0;}
std::map<Key, Value>, boost::unordered_map<Key, Value> という二種類の連想配列が用意されています。前者は二分木、後者はハッシュテーブルによって実装されています。
#include<boost/unordered_map.hpp>// 普段は高速なこっちを使う// #include <map> // BOOST_FOREACH で取得した時にソートされている必要がある場合用#include<boost/assign.hpp>#include<boost/foreach.hpp>#include<iostream>#include<boost/format.hpp>int main(){// 少し面倒なので map の型を typedef するtypedef boost::unordered_map<std::string,int> map_type;// typedef std::map<std::string, int> map_type;// やはり構築は少し面倒。C++の次世代規格で改善されるconst map_type m = boost::assign::map_list_of("mikan",30)("ringo",100)("meron",2000);// m.count(要素) で、その要素が存在しているかチェックできるif( m.count("ringo") ) {// りんごの値段は?constint ringo = m.at("ringo"); std::cout << boost::format("%1%\n") % ringo; }// 要素の書き換えとか map_type m2 = m;// 要素アクセス(なければ作る) m2["iyokan"] =100;// 全部表示する(少し面倒) BOOST_FOREACH(const map_type::value_type x, m2 ) {// x.first がキー、 x.second が値 std::cout << boost::format("%1%: %2%\n") % x.first % x.second; }return0;}
今まで std::cout や boost::format と使ってきた中の、 std とか boost とかです。
これらは名前空間と呼ばれ、同じ名前の関数やクラスの衝突を避けるために使われます。
namespace A{int f() {return23; }}namespace B{int f() {return42; }}#include<iostream>#include<boost/format>int main(){ std::cout << boost::format("%1%\n") % A::f();// 23 std::cout << boost::format("%1%\n") % B::f();// 42}
いちいち何回も std:: や boost:: を書くのが面倒な場合は、 using 宣言というものを行えます。
#include<iostream>#include<boost/format>int main(){using std::cout;// 以降は単に cout で使えるようになるusing boost::format;// 同様に format 単独で使えるようになる cout << format("%1%\n") %"Hello, World";return0;}
さらにusing namespace std; と書くことで、 std名前空間にある全ての名前を std:: と書かずに使うことが出来ますが、意図しない動作になる可能性もあるため、オススメはしません。
この項は随時追加するかもしれません。
return 0; という記述は省略できます。その場合には最後にreturn 0; が呼ばれた事になります。std::cout << "Hello, World\n"; のように書けます。複雑な出力でも[]std::cout << "i の値は" << i << std::endl;[] 的に<<演算子を連続させることで記述できます。ただし読みにくくなるので基本は Boost.Format を使うべきです。なお std::endl は「改行して出力する」という意味です。const int i = 42; std::cout << boost::format("%s\n") % i; というコードもエラーにならず表示されます。引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。