C++

C++はC言語をベースとしたオブジェクト指向プログラミング言語です。C++ の始め方をご紹介します。

gcc

C++ソースファイルの拡張子は .c または .cpp である。

gcc-c++がインストールされていない場合、以下のエラーが出力される。

$ gcc sample.cppgcc: error trying to exec 'cc1plus': そのようなファイルやディレクトリはありません

または

$ gcc sample.cppgcc: error trying to exec 'cc1plus': execvp: No such file or directory

Red Hat Enterprise Linux及びCentOSでgcc-c++をインストールする場合、yumコマンドを使用する。

# yum install gcc-c++

または

$ sudo yum install gcc-c++

コンパイル時に下記のエラーが出力されるときは、libstdc++をリンクする必要がある。

undefined reference to `__gxx_personality_v0'
collect2: ldはステータス 1 で終了しました
gcc sample.cpp -o sample -lstdc++

std::cout

std::cout は、標準出力に関連付けられた出力ストリームオブジェクトである。出力はバッファリングされる。std::coutはstd::ostreamクラスのインスタンスで、グローバル変数である。

std::coutはヘッダostreamで定義されているため、std::coutを使用するプログラムはostreamをインクルードする必要がある。ただし、ostreamはヘッダiostreamからインクルードされているため、iostreamをインクルードしていれば、ostreamを明示的にインクルードする必要はない。

#include <iostream>

main() {
  std::cout << "Hello C++ world!";
}

旧式のC++では名前空間stdは必要ない。この場合はiostream.hをインクルードする必要がある。

#include <iostream.h>

main() {
  cout << "Hello C++ world!";
}

複数の値を「<<」で区切って並べることにより、複数の値を連結して出力することができる。

改行を行うには、「\n」または「std::endl」を出力する。

#include <iostream>

main() {
  int cb = 64;

  std::cout << cb << "bytes" << std::endl;
}

std::cerr

std::cerrは、標準エラー出力に関連付けられた出力ストリームオブジェクトである。std::clogと異なり、バッファリングされない。std::cerrはstd::ostreamクラスのインスタンスで、グローバル変数である。

std::cerrはヘッダostreamで定義されているため、std::cerrを使用するプログラムはostreamをインクルードする必要がある。ただし、ostreamはヘッダiostreamからインクルードされているため、iostreamをインクルードしていれば、ostreamを明示的にインクルードする必要はない。

#include <iostream>

main() {
  std::cerr << "Hello C++ world!";
}

旧式のC++では名前空間stdは必要ない。この場合はiostream.hをインクルードする必要がある。

#include <iostream.h>

main() {
  cerr << "Hello C++ world!";
}

std::clog

std::clogは、標準エラー出力に関連付けられた出力ストリームオブジェクトである。std::cerrと異なり、バッファリングされる。std::clogはstd::ostreamクラスのインスタンスで、グローバル変数である。

std::clogはヘッダostreamで定義されているため、std::clogを使用するプログラムはostreamをインクルードする必要がある。ただし、ostreamはヘッダiostreamからインクルードされているため、iostreamをインクルードしていれば、ostreamを明示的にインクルードする必要はない。

std::cin

std::cinは、標準入力に関連付けられた入力ストリームオブジェクトである。std::cinはstd::istreamクラスのインスタンスで、グローバル変数である。

std::cinはヘッダistreamで定義されているため、std::cinを使用するプログラムはistreamをインクルードする必要がある。ただし、istreamはヘッダiostreamからインクルードされているため、iostreamをインクルードしていれば、istreamを明示的にインクルードする必要はない。

#include <iostream>

main() {
  int a;

  std::cout << "a = ";
  std::cin >> a;
}

旧式のC++では名前空間stdは必要ない。この場合はiostream.hをインクルードする必要がある。

#include <iostream.h>

main() {
  int a;

  cout << "a = ";
  cin >> a;
}

std::endl

std::endlは、改行文字をバッファに出力したうえ、バッファをフラッシュするマニピュレータである。

マニピュレータとは、ストリームに対して何らかの処理を加えるためのオブジェクトである。

std::cout << std::endl

std::flush

std::flushは、出力バッファをフラッシュするマニピュレータである。

マニピュレータとは、ストリームに対して何らかの処理を加えるためのオブジェクトである。

std::cout << std::flush

std::ends

std::endsは、文字列の終端文字 '\0' をバッファに出力するマニピュレータである。

マニピュレータとは、ストリームに対して何らかの処理を加えるためのオブジェクトである。

std::cout << std::ends

std::ws

std::wsは、空白文字を読み飛ばして入力するマニピュレータである。

マニピュレータとは、ストリームに対して何らかの処理を加えるためのオブジェクトである。

std::string str;std::cin >> std::ws >> str;

namespace

C言語において変数名や関数名などの識別子は、一意(ユニーク)である必要があった。C++では名前空間(ネームスペース)が導入されて、識別子は名前空間込みで一意であればよくなった。

たとえば、次のようなC言語プログラムがあったとする。

int emp_no;
int dept_no;

int main(void) {
  emp_no  = 1;
  dept_no = 2;
}

C++では次のように書ける。

namespace emp {
  int no;
}

namespance dept {
  int no;
}

int main(void) {
  emp::no  = 1;
  dept::no = 2;
}

C++ では名前空間を定義することができる。

namespace 名前空間名
{
  // 変数や関数の宣言
  // または、関数やクラスの定義
}

名前空間を定義する例を示す。

#include <iostream>

namespace hello {
  const char world[] = "Hello C++ world!";
}

main() {
  std::cout << hello::world << std::endl;
}

なお、プリプロセッサのマクロ定義は常に大域(グローバル)名前空間に属する。

namespace myspace1 {
  #define BUFSIZE (256)
}

namespace myspace2 {
  #define BUFSIZE (512) // 別の識別子ではなく再定義となる
}

using

using宣言を使って、特定の識別子をスコープ演算子を省略できるようにすることができる。

#include <iostream>

namespace hello {
  const char world[] = "Hello C++ world!";
}

main() {
  using hello::world;

  std::cout << world << std::endl;
}

using namespace

usingディレクティブを使って、特定の名前空間に含まれるすべての識別子を、スコープ演算子を省略できるようにすることができる。

using namespace namespace-name;

namespace-name にはネームスペース名を指定する。

たとえば、using namespace std; とすることで、std::coutやstd::cinをcoutやcinと記述することができるようになる。

#include <iostream>

namespace hello {
  const char world[] = "Hello C++ world!";
}

main() {
  using namespace std;
  using namespace hello;

  cout << world << endl;
}

テンプレート

テンプレートとは、型指定パラメータに基づき関数およびクラスを生成するためのしくみです ("パラメータ化型" と呼ばれることもあります)。テンプレートを使用すると、1 つのクラスを各種のデータ型に適用できるので、型ごとにクラスを作成する必要がなくなります。

たとえば、2 つのパラメータのうち大きい方の値を返すタイプセーフな関数をテンプレートを使わずに作成するには、次に示すように一連のオーバーロード関数を作成する必要があります。

int max(int a, int b) {
  return (a > b) ? a : b;
}

double max(double a, double b) {
  return (a > b) ? a : b;
}

int main(int argc, char *argv[]) {
  int max_int, a_int = 1, b_int = 2;
  double max_dbl, a_dbl = 1.1, b_dbl = 2.3;

  max_int = max(a_int, b_int);
  max_dbl = max(a_dbl, b_dbl);
}

テンプレートを使用すると、次のように重複部分を 1 つの関数テンプレートにまとめることができます。

template <class T> T max(T a, T b) {
  return ( a > b ) ? a : b;
}

int main(int argc, char *argv[]) {
  int max_int, a_int = 1, b_int = 2;
  double max_dbl, a_dbl = 1.1, b_dbl = 2.3;

  max_int = max<int>(a_int, b_int);
  max_dbl = max<double>(a_dbl, b_dbl);
}

テンプレートを使用すると、型を保証しながらソースコードを大幅に小型化し、コードの融通性を向上できます。

std::abs

渡された引数の絶対値を戻り値として返す。

整数の絶対値を求める場合は、cstdlibをインクルードする。

int abs(int n);
long int abs(long int n);
long long int abs(long long int n);
#include <iostream>
#include <cstdlib>

main() {
  std::cout << std::abs(-1) << std::endl;
}

浮動小数点数の絶対値を求める場合は、cmathをインクルードする。

float abs(float x);
double abs(double x);
long double abs(long double x);
#include <iostream>
#include <cmath>

main() {
  std::cout << std::abs(-1.1) << std::endl;
}

std::max

渡された2つの引数の値を比較して、大きい方の値を戻り値として返す。

template <class T> const T& max(const T& a, const T& b);

std::maxの使用例を次に示す。

#include <iostream>
#include <algorithm>

main() {
  std::cout << std::max(1, 2) << std::endl;
}

std::min

渡された2つの引数の値を比較して、小さい方の値を戻り値として返す。

template <class T> const T& min(const T& a, const T& b);

std::minの使用例を次に示す。

#include <iostream>
#include <algorithm>

main() {
  std::cout << std::min(1, 2) << std::endl;
}

std::runtime_error

std::runtime_errorは、例外としてスローされるオブジェクトの型である。プログラムの範囲を超えた事象に起因している、容易に予測できないエラーを報告する。stdexceptヘッダで定義されている。

オーバーロード

2つの数値を足した値を返す関数を作成するとする。C言語の場合は次のようになる。

int add_int(int a, int b) {
  int ans;
  ans = a + b;
  return ans;
}

double add_dbl(double a, double b) {
  int ans;
  ans = a + b;
  return ans;
}

int main() {
  int ans_int, a_int = 1, b_int = 2;
  double ans_dbl, a_dbl = 1.1, b_dbl = 2.3;

  ans_int = add_int(a_int, b_int);
  ans_dbl = add_dbl(a_dbl, b_dbl);
}

2つの関数add_intとadd_doubleは共に「2つの数値を足した値を返す関数」である。同じ処理を行う関数であるので抽象的には同じ関数であるが、C言語の規則により、それぞれ別の関数名にしなければならない。

C++のオーバーロードを用いれば、これらの関数(メソッド)を同じ名前にすることができる。

int add(int a, int b) {
  int ans;
  ans = a + b;
  return ans;
}

double add(double a, double b) {
  int ans;
  ans = a + b;
  return ans;
}

int main() {
  int ans_int, a_int = 1, b_int = 2;
  double ans_dbl, a_dbl = 1.1, b_dbl = 2.3;

  ans_int = add(a_int, b_int);
  ans_dbl = add(a_dbl, b_dbl);
}

どちらの関数(メソッド)が呼び出されるかは、呼び出し側の引数と戻り値の型によってコンパイル時に決定される。

オーバーロードを用いれば同じ関数名(メソッド名)にすることができるが、定義はそれぞれ別々に行わなければならない。C++のテンプレートを用いれば、引数と戻り値の型を抽象化することにより、これらの関数(メソッド)を1つの定義にまとめることができる。

Boost C++ライブラリのインストール

BoostはC++のコミュニティによって公開されているオープンソースのライブラリである。

Boostを使用するには、あらかじめパッケージをインストールしておく必要がある。

Red Hat LinuxおよびCentOSの場合、yum コマンドでBoost C++ライブラリをインストールする。

# yum install boost boost-devel

コンパイル時には、Boostライブラリのインクルードディレクトリを指定する必要がある。

$ gcc -I /usr/include/boost sample.cpp

Microsoft Windows の場合、Boost C++ Libraries のサイトから書庫ファイルをダウンロードする。書庫ファイルを任意のディレクトリに展開する。

filesystem、graphparallel、iostreams、mpi、programoptions、python、regex、serialization、signal、system、thread及びwave以外のライブラリはヘッダファイルをインクルードするだけで使用できる。その他のライブラリを使う場合は、ビルドが必要である。ビルドするには、boootstrapとbjamのコマンドを実行する。コマンドを実行すると、stage\libディレクトリにライブラリファイルが生成される。

bjam

bjamは、Boost C++ライブラリをビルドして指定したフォルダにヘッダファイルとライブラリファイルを保存するコマンドである。

bjam [options] [properties] [install|stage]

installを指定した場合は、ライブラリをビルドして、ヘッダファイルとライブラリファイルを指定したディレクトリへコピーする。stageを指定した場合は、ライブラリをビルドして、ライブラリファイルを指定したディレクトリへコピーする。

bjamのコマンドオプションは次のとおり。

link=link-type
スタティックライブラリを生成するか、ダイナミックリンクライブラリを生成するのかを指定する。カンマで区切って両方を指定することもできる。
bjamのlinkオプション
説明
static スタティックライブラリ
shared ダイナミックリンクライブラリ
bjam link=static,shared
runtime-link=runtime-link-type
Cランタイムライブラリをスタティックリンクするのか、ダイナミックリンクするのかを指定する。カンマで区切って両方を指定することもできる。
bjamのruntime-linkオプション
説明
static スタティックリンク
shared ダイナミックリンク
bjam runtime-link=static,shared
release
リリース版をビルドする。
debug
デバッグ版をビルドする。
--help
bjamのヘルプを表示する。
--toolset=compiler
使用するコンパイラ言語を指定する。コンパイラのバージョンは自動的に検出するが、明示的にバージョンを指定することもできる。
bjamの--toolsetオプション
説明
msvc Microsoft Visual C++
msvc-8_0 Microsoft Visual C++ 8.0
msvc-9_0 Microsoft Visual C++ 9.0
intel Intel C++コンパイラ
gcc GNU Compiler CollectionのC、C++及びObjective-Cコンパイラ
threading=threading-type
シングルスレッドバイナリか、マルチスレッドバイナリかを指定する。カンマで区切って両方を指定することもできる。
bjamのthreadingオプション
説明
single シングルスレッドバイナリ
multi マルチスレッドバイナリ
bjam threading-multi

次に示すオプションは、installを指定した場合にのみ有効である。

--includedir=dir
dirで指定したディレクトリにヘッダファイルを保存する。
--lib-dir=dir
dirで指定したディレクトリにライブラリファイルを保存する。
--prefix=dir
インストール先のディレクトリを指定する。dirに指定したディレクトリにincludeディレクトリとlibディレクトリが作られて、ヘッダファイルとライブラリファイルが保存される。

次に示すオプションは、stageを指定した場合にのみ有効である。

--stagedir=dir
dir で指定したディレクトリにライブラリファイルを保存する。

Boostのライブラリファイル名は次の形式で表される。

libboost_name-vc90-option-version.lib

name はfilesystemやthreadなどのライブラリ名である。

option はライブラリの種類を表している。

Boost C++ライブラリの種類
option 説明
mt マルチスレッド版
mt-gd マルチスレッド、デバッグ版
mt-s マルチスレッド、スタティックCランタイム版
mt-sgd マルチスレッド、スタティックCランタイム、デバッグ版
s シングルスレッド版

version はBoost C++ライブラリのバージョンである。

共有オブジェクト(*.so)にBoostのスタティックライブラリをリンクする場合は、bjamのコマンドオプションに「cxxflags=-fPIC」を指定してBoostライブラリをビルドする。

$ ./bjam link=static --prefix=/home/horiuchi/local cxxflags=-fPIC install