Movatterモバイル変換


[0]ホーム

URL:


野良C++erの雑記帳

C++/Boost 基礎文法最速マスター

参考:http://d.hatena.ne.jp/faith_and_brave/20100201/1264997004


C++0xになると、C++03 でごちゃごちゃした部分がだいぶすっきり書けるようになる」
らしいですが、C++0xを待たなくてもBoostを使えばだいぶすっきり書けるので、
BoostでのC++入門はこんな感じだよー、という気持ちで以下略。
この記事はC言語をある程度理解していることが前提です。


1.Hello World

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";

このように引数を何回も使い回したりできます。
使い回さない場合でも、この書き方なら いちいち型を指定する必要がないのでオススメです。

2.

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;}

3. 変数

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;}

4. 関数

関数を使うことで処理に名前を付けることが出来ます。
"戻り値の型 関数名( 仮引数リスト){ 処理}" のように書きます。

// 引数を取らない例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<[] 戻り値の型( 引数型リスト)[]>[]"となります。

5.

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>をインクルード)

6. 基本演算

C++/Boost の演算子は殆どC言語と同様です。
ただし>>演算子および<<演算子は、従来の意味(ビットシフト)に加えて「ストリーム入出力」という意味を持ちます。
また %演算子も、従来の意味(剰余演算)に加えて「フォーマット出力」という意味を持ちます。
これらは「演算子多重定義」という仕組みによって実現されていますが、ライブラリ製作者以外はその仕組を学習する必要はないでしょう。

7. 参照

参照は変数に別名を与えます。

// 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;}

8. 制御構文

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;}

9. クラス

Cで言う構造体です。C++/Boost では、クラスを使うことで新しい型を定義することが出来ます。
今まで出てきた std::string や boost::function などは全てクラスです。これらは基本型と殆ど同じように扱うことが出来る他、 "変数名.メンバ関数名" と書くことにより、そのクラス専用に用意された関数を使うことが出来ます。

const std::string s ="abc";std::cout << boost::format("%1%\n") % s.length();// s の長さを求める

ユーザ側で定義することも出来ますが、ライブラリ側で定義されたクラスを使うだけでも、十二分にプログラミングは可能です。
自分でクラスを作ることは落とし穴が多いので、慣れないうちはライブラリ定義のクラスを使いましょう。

10. テンプレート

boost::function<int()> 等で既に触れられている物です。
任意の型に対して同じことをさせたい場合に用います。
例として簡単なテンプレート関数を作ってみると、

template<typename T>void disp( T x ){  std::cout << boost::format("%1%\n") % x;}

このように書くことで、disp 関数は、あらゆる型の変数 x を受け入れることが出来るようになります。
例えば、

disp<int>(23);

のように呼び出すと、dispTint で置き換えた

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;[] のように使います。
その場合、角括弧は省略不能です。

11. 固定長配列

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;}

12. リスト

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;}

13.連想配列

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;}

14.名前空間

今まで 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:: と書かずに使うことが出来ますが、意図しない動作になる可能性もあるため、オススメはしません。

15. その他の細かいこと

この項は随時追加するかもしれません。

  • main 関数に限り、最後のreturn 0; という記述は省略できます。その場合には最後にreturn 0; が呼ばれた事になります。
  • 実は単純な出力なら boost::format を使わなくてもstd::cout << "Hello, World\n"; のように書けます。複雑な出力でも[]std::cout << "i の値は" << i << std::endl;[] 的に<<演算子を連続させることで記述できます。ただし読みにくくなるので基本は Boost.Format を使うべきです。なお std::endl は「改行して出力する」という意味です。
  • 実は boost::format の型指定文字 %s は型非依存です。const int i = 42; std::cout << boost::format("%s\n") % i; というコードもエラーにならず表示されます。
検索

引用をストックしました

引用するにはまずログインしてください

引用をストックできませんでした。再度お試しください

限定公開記事のため引用できません。

読者です読者をやめる読者になる読者になる

[8]ページ先頭

©2009-2025 Movatter.jp