C++0xになると、C++03でごちゃごちゃした部分がだいぶ
すっきり書けるようになるので、C++0xでの入門はこんな感じになるよー、
という気持ちで書きました。
1. Hello World
C++0xでの入出力には、IOStreamというものを使用します。
<<演算子でどんどんつないでいきます。
以下のプログラムの読み方は
「標準出力(cout)に"Hello World"という文字列と、改行(endl)を出力する」
です。
#include <iostream> int main() { std::cout << "Hello World" << std::endl; return 0; }
Hello World
coutとendlを使用するには、
#include <iostream>
のように、
2. コメント
行コメント
std::cout << "Hello"; // これはコメント
ブロックコメント
/* これは コメント です */ std::cout << "Hello";
3. 変数
C++0xでは「const 型 変数名 = 値」のようにして変数を宣言します。
constを付けない場合、変数の値を変更でき、初期値も省略できますが、
バグの温床となりえるので、基本的にconstを付けることをオススメします。
#include <string> #include <vector> int main() { const int a = 3; // 整数型(int)の変数(a)を宣言し、3という値を割り当てる const std::string s = "abc"; // こちらは文字列型(std::string) const std::vector<int> v = {1, 2, 3}; // リスト(std::vector)。ここでは整数型(int)を格納できる return 0; }
4. 関数
関数は処理に名前を付けるために使用します。
基本的な文法は2種類あり、戻り値の型を前置するものと後置するものがあります。
これはスタイルで使い分けてください。
// 戻り値の型を前置する構文 // 「戻り値の型 関数名(パラメータの型 パラメータ名)」 int square(int x) { return x * x; }
// 戻り値の型を後置する構文 // 「auto 関数名(パラメータの型 パラメータ名) -> 戻り値の型」 auto square(int x) -> int { return x * x; }
呼び出し側:
const int s = square(3); // s == 9
戻り値で何も返さない場合は、voidという特別な型が必要になります。
入出力などで使用します。
void disp(const std::string& s) { std::cout << s << std::endl; } disp("Hello World");
5. 型
C++0xには以下のような型が用意されています(一部です)。
基本データ型
int : 整数型(0, 1, 2, 3, -1, etc...) char : 文字型('a', 'b', 'c', '1', etc...) double : 浮動小数点数型(0.1, 3.14, etc...) bool : 論理型(true, false)
複合データ型
std::array<T, N> : 配列(<array>をインクルード) std::vector<T> : リスト(<vector>をインクルード) std::string : 文字列(<string>をインクルード) std::map<Key, Value> : 辞書(<map>をインクルード) std::set<T> : 集合(<set>をインクルード) std::pair<T, U> : 組(<utility>をインクルード) std::tuple<Args...> : タプル(<tuple>をインクルード)
6. 基本演算
四則演算
const int num = 3 + 2; // num == 5 const int num = 3 - 2; // num == 1 const int num = 3 * 2; // num == 6 const int num = 5 / 2; // num == 2 const int num = 5 % 2; // num == 1
論理演算
const int a = 1; const int b = 2; const bool r = a == b; const bool r = a != b; const bool r = a < b; const bool r = a > b; const bool r = a <= b; const bool r = a >= b; const bool p = true; const bool q = false; const bool r = p && q; const bool r = p || q;
7. 参照
参照は変数に別名を付けます。
int a = 1; int& b = a; // bはaを参照する b = 2; // bの参照先であるaを2に書き換える
関数の戻り値が複数ある場合などに使用します。
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); // a == 127 // b == 0 // c == 0 // d == 1
8. 制御構文
if/else文、もしくは条件演算子を使用することで条件分岐できます。
// if/else文を使用した場合 int abs(int x) { if (x >= 0) { return x; } else { return -x; } }
// 条件演算子を使用した場合 int abs(int x) { return x >= 0 ? x : -x; }
switch文は、値と処理の対応表として使用できます。
int next_scene(int scene) { switch (scene) { case title: return menu; case menu: return game; case game: return gameover; } throw std::invalid_argument("不正なシーン"); }
while文は、条件を満たす間ループするための構文です。
int retry = 0; while (!connect()) { std::cout << "まだ接続できない" << std::endl; ++retry; }
for文は、while文をより書きやすくした構文です。
for (int retry = 0; !connect(); ++retry) { std::cout << "まだ接続できない" << std::endl; }
さらに、データ構造を1要素ずつ処理するための範囲for文というものもあります。
const std::vector<int> v = {1, 2, 3}; for (const int x : v) { std::cout << x << std::endl; }
9. テンプレート
std::vector
あらゆる型を格納するデータ構造や
あらゆる型で振舞うアルゴリズムを作るときに
型のプレースホルダーとしてテンプレートというものを使用します。
template <class T> void disp(T x) { std::cout << x << std::endl; }
int型の値/変数を渡した場合、もしくは明示的にintを指定した場合
disp(3); disp<int>(3);
disp関数のTはintに置き換えられます。
void disp(int x) { std::cout << x << std::endl; }
double型の場合も同様です。
disp(3.14); disp<double>(3.14);
void disp(double x) { std::cout << x << std::endl; }
10. リスト操作
#include <vector> const std::vector<int> v = {1, 2, 3}; const int front = v.front(); // front == 1 : 先頭要素を取得 const int back = v.back(); // back == 3 : 最後尾要素を取得 const int second = v[1]; // second == 2 : 添字を指定したランダムアクセス // 全ての要素を出力 for (const int x : v) { std::cout << x << std::endl; } // 破壊的な操作 : constにできない std::vector<int> v = {1, 2, 3}; v.push_back(4); // v == {1, 2, 3, 4} 末尾に要素を追加する v.pop_back(); // v == {1, 2, 3} 末尾の要素を削除 v.assign({4, 5, 6}); // v == {4, 5, 6} 要素の再割り当て
11. 連想配列の操作
#include <map> #include <string> const std::map<std::string, int> m = { {"Akira", 24}, // {キー, 値} {"Millia", 16}, {"Johnny", 38} }; const int age = m.at("Akira"); // age == 24 // 破壊的な操作 std::map<std::string, int> m = { {"Akira", 24}, {"Millia", 16}, {"Johnny", 38} }; m["Sol"] = 150; // 要素の追加。キーがすでにあったら値を書き換え
12. アルゴリズム
データ構造を操作するアルゴリズムが多数用意されています。
多くのアルゴリズムは破壊的です。
#include <iostream> #include <vector> #include <algorithm> void disp(int x) { std::cout << x << std::endl; } int main() { const std::vector<int> v = {1, 2, 3}; // for_each : 指定された範囲の要素全てに関数を適用する std::for_each(v.begin(), v.end(), &disp); // find : 範囲内の指定された値を検索する decltype(v)::const_iterator it = std::find(v.begin(), v.end(), 2); if (it != v.end()) { std::cout << "見つかった:" << *it << std::endl; } else { std::cout << "見つからなかった!" << std::endl; } // 破壊的な操作 std::vector<int> vv = {3, 1, 4}; // reverse : 範囲を反転する std::reverse(vv.begin(), vv.end()); // sort : 並び替え std::sort(vv.begin(), vv.end()); return 0; }
13. ラムダ式
アルゴリズムでは、各要素を処理するための関数を作ることが多くなりますが
関数をわざわざ作るのがめんどくさい場合に、ラムダ式というものを使って
その場に関数を作れます。
#include <iostream> #include <vector> #include <algorithm> int main() { const std::vector<int> v = {1, 2, 3}; std::for_each(v.begin(), v.end(), [](int x) { std::cout << x << std::endl; }); return 0; }
アルゴリズムのあたりはOvenのようなRangeライブラリが入れば
もっとすっきり書けそうですが、C++0xでもだいぶ書きやすくなりましたね。