Software_CPP3

CPP(その3)

┌──┐
│目次│
├──┘
│☆文法(続き)
◆名前空間 name
◆入出力 io
◆例外処理 excep
◆TIPS tips
◆プリプロセッサ prepro
☆ライブラリ lib
◆ランタイムライブラリ runtime
◆標準テンプレートライブラリ stl
☆処理系限界 limit
◆処理系限界の推奨値 limit2
☆コーディングポリシー coding
◆ヘッダ header
◆差分プログラミング incrementalprog
◆ポリモーフィズム poly


◆名前空間
name space

_◇名前空間の定義

例)

namespace Sample {
    int a;
    void func();
}

※名前空間によって、変数、関数のスコープを名前空間に限ることができる。

※名前空間の外からのアクセスには
│ 名前空間名::
を変数、関数に指定する。

_◇省略のための宣言

例)std名前空間のすべての変数、関数に対する名前空間指定の省略
using namespace std;

例)名前空間の特定の変数の名前空間指定の省略
using Sample::a;
a = 10;

_◇名前なし名前空間

※名前なし名前空間で定義された変数、関数
⇒その定義を含むソースファイル内でのみ利用できる
⇒スコープ限定のための定石


◆入出力

_◇ストリーム
stream

※入出力をささえる概念
⇒さまざまな装置に対する入出力を統一的な方法で扱うことができる

※C++の標準入出力ストリームcin, cout, cerr, clogは、C言語の標準入力ストリームstdin, stdout, stderrと同期をとって処理されることが保証されている。
⇒ios_baseクラスの静的メンバ関数sync_with_stdioにより、同期を有効、無効と切り替えできる

※標準ストリーム
standard stream
以下の4ストリームの総称
⇒char型の文字が流れる

①標準出力ストリーム cout
standard output stream
│ stdinに結び付けられたistream型標準入力ストリーム

②標準入力ストリーム cin
standard input stream
│ stdoutに結び付けられたostream型標準出力ストリーム

③標準エラーストリーム cerr, clog
standard error stream
│ stderrに結び付けられたostream型標準出力ストリーム
│ cerr バッファリング無し
│ clog バッファリング有り

※ワイド文字型ストリーム
wchar_t型の文字ストリーム
│ wcin
│ wcout
│ wcerr
│ wclog

※ストリーム型はクラステンプレートである
│ basic_istream<>
│ basic_ostream<>
を明示的に特殊化したテンプレートクラス
⇒char用に特殊化 narrow stream
⇒wchar_t用に特殊化 wide stream

※抽出子(extractor) >> と挿入子(inserter) <<
⇒シフト演算子の多重定義

抽出子(extractor) >> はbasic_istream&型を返却
挿入子(inserter) << はbasic_ostream&型を返却
⇒挿入子は呼び出し元ストリームへの参照を返却するので連続適用できる。
例)
│ cout << “n=” << n;

│ (cout.operator<<(“n=”)).operator<<(n);
と同じ

※文字の入出力(C言語形式文字列の入出力を含む)を行う関数
⇒非メンバ関数として実装されている

※リダイレクト
OSのリダイレクト機能により標準入力ストリームと標準出力ストリームの接続先を変更できる。

_◇標準出力
standard output

※標準ライブラリ <iostream>

※cout 標準出力への出力ストリーム
ostreamクラスのオブジェクト

例)
cout << “Hello!;

cout << 101;

※ユーザー定義クラスで>>や<<演算子をフレンド関数としてオーバーロードすることでそのクラスを入出力することができるようになる。
⇒<<演算子は左結合なので、ostreamクラスへの参照を戻り値とする必要がある
⇒<<演算子は左結合する相手がostreamクラスのオブジェクトなのでメンバ関数にはできないのでフレンド関数となる

例)

class Car {
    ...
    friend ostream& operator<<(ostream& out, Car& c);
};

ostream& operator<<(ostream& out, Car& c)
{
    out << "Number" << num << ":" << "GAS" << gas;
    return out;
}

_◇標準入力
standard input

※標準ライブラリ <iostream>

※cin 標準入力
istreamクラスのオブジェクト
⇒istreamクラスで>>をオーバーロードしている
⇒入力用にオーバーロードされた>>演算子を抽出演算子と呼ぶ
etraction operator

例)
cin >> number;

※入力された数字を変数に代入する

※変数に収まらないような無効な値が入力されればエラーとなる。(無効な値となる)

※char型変数には1文字のみ

※空白は読み飛ばされる。

※Enterキーで入力した値が読み取られ、処理が再開する。

_◇標準エラーデバイス
cerr

※標準出力がリダイレクトされても、標準エラーデバイスへの出力はリダイレクトされないので画面に出る。

_◇挿入演算子
insertion
<<

出力ストリームの中に文字を挿入する演算子
文に複数の演算子を使える。
挿入演算子があるたび、単純に付加する

_◇抽出演算子
extraction
>>

必要な変数の数だけ、抽出演算子を繰り返す

cin >> first >> second >> third;

※スペース、タブ、キャリッジリターンが値と値の区切りとして使われる

_◇一文字入出力

※istreamクラス get()メンバ関数
空白を含む1文字を読み取る
⇒Windows環境ではCTRL-Zで偽、UNIX系ではCTRL-Dで偽が返る

例)
cin.get(ch);
⇒chは読み取った文字を格納する変数

※ostreamクラス put()メンバ関数
1文字を出力する
例)
cout.put(ch);

_◇1行入力

getline(入力ストリーム, 文字列変数);

⇒行末の改行文字は切り捨てられ、文字列には格納されない。

_◇特殊文字
\n 改行
│ 文字列” “の中に含める
│ 改行文字 ‘\n’ を挿入演算子に与える
endl 行末記号

\a ベル
\b バックスペース
\f フォームフィード
\n 改行
\r 復改
\t 水平タブ
\v 垂直タブ
\\ 円記号
\? 疑問符
\’ 引用符
\” 二重引用符
\0 ヌル文字
\ooo 8進数
\xhhh 16進数

_◇出力幅制御、フィル

※ostreamクラスのメンバ関数による指定は、最初の出力だけに有効

※ostreamクラス width()メンバ関数
出力幅指定
構文)
│ width(出力幅);
例)
│ cout.width(3);
⇒指定幅より出力すべき対象の幅が大きいときは、そちらの幅で出力される

※ostreamクラス fill()関数
例)
│ cout.fill(‘_’);
⇒出力幅でスペースの代わりにつかわれるフィル文字を指定

_◇精度指定

※ostreamクラスのメンバ関数による指定は、最初の出力だけに有効

※ostreamクラス precision()関数
浮動小数点数を出力するときの有効桁数(精度)を指定する

例)
cout.precision(num);

_◇書式状態フラグの設定

※ostreamクラス setf()メンバ関数
書式状態フラグを設定する
構文)
│ setf(書式状態フラグ)

※ostreamクラス unsetf()メンバ関数
書式状態フラグの設定解除
構文)
│ unsetf(書式状態フラグ)

※書式状態フラグ
ios::boolalpha ブール型の入出力をtrue, false文字列で行う
ios::adjustfield 出力位置設定
ios::basefield 基数設定
ios::floatfield 小数の記法設定
ios::skipws 空白文字スキップ
ios::left 左寄せ
ios::right 右寄せ
ios::internal 符号左、数値右寄せ
ios::dec 10進
ios::oct 8進
ios::hex 16進
ios::showbase 8進の場合、頭0、16進0x
ios::showpoint 末尾に0
ios::showpos 符号(+)表示
ios::scientific 科学表記
ios::fixed 固定小数点形式
ios::uppercase 英大文字

※使用例
cout.setf(ios::left);

_◇マニピュレータ
manipulator

※改行出力
endl
⇒エスケープシーケンス’\n’を出力する代わりになる。

※基数変更マニピュレータ
⇒挿入演算子で出力ストリームに設定すると、プログラム末か、別な出力修飾子を使うまで効果が持続する。
※dec
│ 数値を10進に

※oct
│ 数値を8進に

※hex
│ 数値を16進に

#include <iomanip.h> 必要なマニピュレータ

※stew修飾子
stew(3)のように最小文字数を指定し、挿入演算子で出力ストリームに設定する。指定は直後のみ有効。指定の幅以上の桁数が表示に必要なら効果はない。

※setfill(文字)
⇒設定した書式は変更しない限り保持される

※setprecision(整数値)
⇒設定した書式は変更しない限り保持される

※書式状態フラグの操作もマニュピュレータでできる
例)
cout << dec;
cout << boolalpha;

_◇ファイル入出力

※fstream.hを読み込み、ofstream, ifstreamクラスを利用する

例)

#include <fstream>
#include <iostream>
using namespace std;

int main()
{
    ofstream fout("t.txt");
    if (!fout) {
        cout << "CAN NOT OPEN.\n";
        return 1;
    }
    else
        cout << "FILE OPENED.\n";
    fout.close();
    cout << "FILE CLOSED.\n";
    return 0;
}

⇒ostreamクラスのfoutオブジェクトを作る
⇒ファイルがオープンできないと、foutはfalseとなる

※入出力の基本クラスiosのメンバ関数によってもストリームの状態を知ることができる。

eof() ファイルの終わりに達したか?
fail() エラーが発生したか?
bad() エラーが発生したか?
good() ストリームが正常か?
rdstate() エラーの値を調べる

例)
fout.fail()

例)

#include <string>
#include <fstream>
#include <iostream>

using namespace std;

int main()
{
    ifstream fis("HELLO.TXT");

    if (!fis)
        cerr << "\aFile can not be opend.\n";
    else {
        while (true) {
            string text;
            fis >> text;
            if (!fis) break;
            cout << text << '\n';
        }
    }
}

⇒入力ストリーム末の判定は!ファイルオブジェクトで判定できる。
⇒C言語文字リテラルでなくstring型文字に対する挿入子、抽出子は<string>のインクルードが必要
⇒抽出子によるstring読み込み時には空白が区切りとなるので、単語単位となる。
⇒行単位の入力にはgetlineを使用する

_◇ファイルストリーム・クラス

<fstream>のインクルード必要
⇒fstream内部でiostreamがインクルードされる。

①fstream
│ 入出力
②ifstream
│ 入力
③ofstream
│ 出力

※ファイルのオープン
コンストラクタもしくはメンバ関数openによる

例)
│ ofstream fs(“abc.txt”);

│ ofstream fs;
│ fs.open(“abc.txt”);

⇒ファイル名引数は const char*なので、文字列リテラルをそのまま渡せる
⇒オープンの成功、不成功の判定
│ if (fs.is_open())
│  オープンされているかどうかの判定
│ if (!fs.fail())
│  各種操作の失敗を保持するfailbitを調べる
│ if (fs)
│  変換関数operator void*()により、
│  ストリームのインスタンスはfailなら空ポインタ
│  そうでないなら空でないポインタを返す

※ファイルのクローズ
メンバ関数 close()による

例)
fs.close();
⇒ストリームが消滅するときに、自動的にデストラクタが呼び出され、クローズされるので、close()を明示的に呼び出す必要は限定される

_◇ファイルの存在確認

例)

#include <string>
#include <fstream>
#include <iostream>

using namespace std;

bool file_exist(const char* filename)
{
    ifstream fis(filename);
    return fis.is_open();
}

int main()
{
    string file_name;
    cout << "file name? ";
    cin >> file_name;

    cout << (file_exist(file_name.c_str()) ? " Exist.\n" : " not Exist.\n");
}

⇒実行終了時にifstreamのデストラクタにより自動的にクローズされるのでここではクローズしていない。
⇒入力ストリームとしてファイルをオープンするならファイルの内容は保持されるが、出力ストリームとすると内容が消去される。

_◇コマンドライン入力
command line argument

構文)コマンドライン引数を受け取るmain

int main(int argc, char* argv[])
{

}

argc 入力した文字列の個数
│ プログラム名そのものを1個目の文字列として扱う
argv[] 入力した文字列
│ argv[0]はプログラム名の文字列


◆例外処理
exception handling

※エラーに対する対処法は、部品の開発者ではなく利用者によって決められるべき場合が多い

※例外安全
発生した例外に対して適切な処理をすること

※例外中立
全ての例外を呼び出し側に伝えること

_◇エラーの検出

例1)配列の領域を超えるアクセスチェック

int& IntArray::operator[](int i)
{
    if (i < 0 || i >= nelem)
        //error
    else
        return vec[i];
}

_◇例外処理

例外 exception
送出 throw
捕捉 catch

構文)

try {
    例外を検出する処理(try block)
    throw 例外;
}
catch (型) {
    例外時の処理;
}


⇒tryブロックないでの例外発生あるいはthrowにより、ある値を例外として送出する
⇒送出された例外は、catchブロックで受け取られ、処理される。
⇒catchブロック=例外ハンドラ(exception handler)

構文)

try {
    例外を検出する処理
    throw 例外1;

    throw 例外2;
}
catch (例外1の型) {
    例外1の時の処理;
}
catch (例外2の型) {
    例外2の時の処理;
}

※throwはtryブロックから呼び出された関数からも送出可能。
※関数やクラスの中では例外をスローするだけにしておき、外側でエラーを捕まえて対処することができる
⇒例外ハンドラは複数個を連続しておくことが出来る。

※例外宣言 exception declaration
⇒catch節が複数あるときはその順番で捕捉するかどうか判定する。
① catch (Exp)
│ 引数に名を与えず、捕捉する例外の型のみ指定する
│ ⇒その型の値などの情報はとりだせない

② catch (Exp v)
│ 値を受け取る。基本型の値を受け取る場合

③ catch (const Exp& r)
│ 参照で受け取る。クラス型の例外を受け取る。参照で受けることでコピーが発生することを避ける。

④ catch (…)
│ 未捕捉の全例外を捕捉
│ ⇒受け取る例外オブジェクトの参照は不可能

※例外ハンドラの中で例外処理を完結できない場合は、再送出も可能。
※main関数に戻っても例外が捕捉されない場合は、terminate関数が呼び出される。

_◇送出式と例外オブジェクト
throw expression

throw
⇒オペランド無
⇒例外の再送出(rethrow)用

throw 式
⇒オペランドの式により、送出される例外の種類が決定される
⇒式の型を持つ例外オブジェクト(exception object)と呼ばれる一時オブジェクトを初期化し、そのコピーが送出される。

※例外オブジェクトの型
①式の静的な型から最上位のcv修飾子を取り除く
②配列の場合はポインタ、関数の場合は関数へのポインタへ調整する
③不完全型、不完全型へのポインタや参照は許されないが、void*は許される。

※例外の再送出
捕捉した例外を処理することがそのハンドラでできないときは、例外を再送出し、上位のハンドラに処理をまかせる
⇒オペランド無のthrowにより同じ例外として投げてもよいし、別な例外として投げてもよい。
⇒再送出すべき例外が存在しない場合、<exception>terminate関数が呼び出される

※例外の送出
⇒プログラムの制御はそこには戻ってこない
⇒プログラムの流れがブロックの外へ飛び出す際に、その前に構築された局所オブジェクトは解体される(適切にデコンストラクタを作成してあれば)

_◇例外の階層化

※例外クラスを階層化することで、例外の分類や構造化が可能となる
⇒派生クラスの例外を基底クラスでまとめて受け止めることができる
⇒基底クラスで受け取ると、スライシングにより派生クラス独自の情報が切り捨てられる。
⇒例外クラスを階層化するときは、仮想関数を導入し、多相的クラスとして基底クラスからでも派生クラスの情報にアクセス可能とするとよい。

※例外クラスの階層構造を利用する例外ハンドラでは
⇒より上位の派生クラスをより先頭側に配置する
⇒基底(下位)側クラスをより末尾側に配置する。

_◇<exception>ヘッダterminate関数

void terminate();
引数と返却値がvoidである関数を呼びだしてプログラムを強制終了する。
デフォルトでは、標準ライブラリ void abort()を呼び出す

※set_terminate関数
⇒terminate関数実行時に呼ばれる関数を登録する。
terminate_handler set_terminate(terminate_handler f) throw();

登録例)

void exception_error()
{
    cout << "Exception.\n";
    abort();
}

...

set_terminate(exception_error);

_◇main関数全体を対象とする万能例外ハンドラ

init main () try {
} catch (…) {
}

_◇例外指定
exception specification

※第3版C++では推奨されない
関数が送出する可能性がある例外を明示できる。
⇒明示した以外の例外を送出しようとするとbad_exception例外が送出され、unexpected関数が呼び出される。
⇒例外型のチェックはコンパイル時ではなく、実行時に行われる。

構文)
返却値型 関数名(仮引数宣言) throw(例外型並び)
{

}
⇒例外型並びはコンマで区切って並べる
⇒例外型並びが空だといかなる例外も送出しない線gンとなる

_◇関数本体全体での例外の捕捉

例)

void abc(IntArray& x) try {
    g(x);
} catch (ErrA) {
    ...
}

_◇例外処理クラス

標準ライブラリで提供される各種の例外クラス

<exception>
│ exception 全ての例外クラスの基底クラス
│  bad_exception 例外指定違反
<new>
│  bad_alloc 記憶域確保違反
<typeinfo>
│  bad_cast 動的キャスト失敗
│  bad_typeid typeid式中に空ポインタが含まれる
<stdexcept>
│  logic_error 論理エラー(実行前に検出可能)
│  domain_error ドメインエラー
│  invalid_argument 不正実引数
│  length_error オブジェクトが大きすぎる
│  out_of_range 実引数の値が範囲外
│  runtime_error 実行時エラー
│  range_error 内部計算による範囲エラー
│  overflow_error 算術的オーバフロー
│  underflow_error 算術的アンダーフロー
<ios>
│  ios_base::failure 入出力ストリームライブラリエラー

① exceptionクラス
標準C++ライブラリが送出する全ての例外型の基底クラス
定義例)

class exception {
public:
    exception() throw(); //デフォルトコンストラクタ
    exception(const exception&) throw(); //コピーコンストラクタ
    exception& operator=(const exception&) throw(); //代入演算子
    virtual ~exception() throw();
    virtual const char* what() const throw();
};

⇒仮想メンバ関数 what
⇒処理系依存。文字列を返す。
※すべてのメンバ関数の例外指定は throw()であり、例外を一切送出しないと宣言している。

②bad_exceptionクラス
例外指定に対する違反を表す。
関数が例外指定で宣言していない例外を送出したときにbad_exception例外が送出される。
⇒bad_exception例外⇒unexpected関数呼び出し⇒terminate関数呼び出し⇒異常終了

※unexpected関数の挙動はset_unexpected関数で変更できる。
※Visual C++は例外指定に対応していないので即異常終了となる。
※MinGW上のg++でも同様。

③bad_allocクラス
new演算子による記憶領域の確保に失敗した時
⇒set_new_handler関数で指定された関数が呼び出される。

④bad_castクラス
動的キャストに失敗

④bad_typeidtクラス
typeid式中に空ポインタが含まれるとき

※標準例外:論理エラー

<stdexcept>ヘッダ

※論理エラー
logical error
クラス独自の公開メンバは、const string&型の引数を受け取るコンストラクタのみ
⇒受け取った文字列は、whatで受け取れる。

※logic_errorクラスの派生クラスには、引数のないデフォルトコンストラクタが定義されていないので、必ず引数を渡す必要がある。

※実行時エラー
runtime error
クラス独自の公開メンバは、const string&型の引数を受け取るコンストラクタのみ
⇒受け取った文字列は、whatで受け取れる。

※runtime_errorクラスの派生クラスには、引数のないデフォルトコンストラクタが定義されていないので、必ず引数を渡す必要がある。


◆TIPS

_◇乱数の生成

rand関数 … 0~RAND_MAX以下の値を生成

<cstdlib>で定義されるRAND_MAXの値は処理系依存だが、最低でも32767

#include <cstdlib>
using namespace std;
int x = rand();

※乱数の種の変更
srand()
⇒同じ種からは同じ系列が発生する
定石⇒srandに対して現在時刻を与える

#include <ctime>
#include <cstdlib>
using namespace std;

srand(time(NULL));

time関数の返却値はtime_t型の現在時刻

_◇オブジェクトのコピーの抑止

コピーコンストラクタと代入演算子の宣言を非公開部で行う。
⇒クラス型の利用者から隠すことで、オブジェクトのコピーが抑止される。

_◇g++での日本語出力

① minGW上のg++でのSJISの使用
デフォルトがutf-8なので -finput-charsetでソース文字集合を、-fexec-charsetで実行文字集合を指定してコンパイルする SJISの場合文字セットはcp932とする

例)
$ g++ -finput-charset=CP932 -fexec-charset=CP932 -o jtest.exe jtest.cpp

例)jtest.cpp

#include <iostream>

using namespace std;

int main(void)
{
    cout << "あほ" << endl;
    return 0;
}

_◇コマンドライン引数

int main(int argc, char** argv)
{

①argc
│ プログラム名自身と仮引数をあわせた個数

②argv
│ argv[0] プログラム名文字列を指す
│ argv[1]以降は、仮引数文字列をそれぞれ指す
│ 末尾に空ポインタが存在する


◆プリプロセッサ

_◇#include文

例)
include <iostream.h>


☆ライブラリ


◆ランタイムライブラリ

※Cのライブラリ関数の利用
CのヘッダファイルをC++で使う場合には、先頭にcをつけて、後尾の.hを省いたヘッダファイルを使用する。
⇒名前空間

そのままでは、std::printf(。。。などとする

using namespace std;
により以降 std:: を省略できる。

_◇cstdlib
C++の標準ライブラリ(stdlib)を使用するときに用いる

#include <cstdlib>

EXIT_SUCCESS
EXIT_FAILURE
NULL
RAND_MAX

CHAR_BIT
│ char型の構成ビット数を返すマクロ

abort()
│ プログラムの異常終了

abs()
int atexit(void (*func)(void));
│ 正常終了時に自動実行する関数を登録する
│ ⇒登録できるのは引数無、返り値無の関数のみ
│ ⇒最低でも32個登録でき、登録の逆の順序で実行される
│ ⇒登録が成功すれば0をそうでなければ0以外を返す

atof()
atoi()
atol()
bsearch()
│ ソート済配列から探索
│ あらゆる型からの探索が行える
│ void* bsearch(const void* key, const void* base,
│  size_t nmemb, size_t size,
│  int (*compar)(const void*, const void*));
│ オブジェクト配列(先頭要素base, 要素数nmemb, 要素の大きさsize)から
│ keyが指すオブジェクトに一致する要素を探索する
│ comparが比較関数を指す。比較関数はkeyオブジェクトへのポインタを第一引数
│ 配列要素へのポインタを第2引数として呼び出される。keyオブジェクトとの
│ 小なり、一致、≧に応じて、負、0、正の整数を返す(昇順配列の場合)。
│ ⇒比較結果を正、0、負とすれば降順配列ででも探索できる。
│ ⇒一致要素がないときは空ポインタを返す。
│ ⇒見つけた場合はその要素へのポインタpを返す。
│ ⇒見つけられた要素の添え字は先頭要素へのポインタxをつかって
│  p-x
│ で求められる。
│ ⇒バイナリサーチをアルゴリズムとして使うか否かは処理系依存
│ 比較関数例)
│ int int_cmp(const int* a, const int* b)
│ {
│  return *a < *b ? -1 : *a > *b ? 1 : 0;
│ }
│ ⇒比較関数は必ずしも-1や1を返す必要はない

calloc()
div()
exit()
│ プログラムの正常終了
│ ⇒main関数でreturnしてもよい

free()
getenv()
labs()
ldiv()
malloc()
mblen()
mbstowcs()
mbtowc()
qsort()
│ ソートを行う
│ void* qsort(void* base, size_t nmemb, size_t size,
│  int (*compar)(const void*, const void*));
│ オブジェクト配列(先頭要素base, 要素数nmemb, 要素の大きさsize)を
│ comparが指す比較関数に従って整列する
│ 比較関数は第1引数が、第2引数より小/等/大のとき、
│ 負、0、正の整数を返す(昇順にソートする場合)。
│ ⇒2つの要素が等しいとき、整列された配列内での順序は既定されない
│ ⇒クイックソートを使うかどうかは処理系依存

rand()
realloc()
srand()
strtod()
strtol()
strtoul()
system()
wcstombs?
wctombs?

_◇iostream
標準入出力の使用のために必要

ostreamクラス
istreamクラス

_◇iomanip
stew()などのマニピュレータの使用に必要

_◇fstream
ファイル入出力に必要

ofstreamクラス
│ ostreamクラスの派生クラス

ifstreamクラス
│ istreamクラスの派生クラス

_◇time

※time_t型
例)
│ time_t system_time;

※time()
例)
│ system_time = time(NULL);

※ctime()
例)
│ ctime(&system_time)
可読できる文字列に変換する

_◇ctime

①time_t型の現在時刻を取得
time_t current = time(NULL);

②struct tm型のローカルタイムへ変換
struct tm* lct = localtime(&current);

_◇math

※sqrt()

_◇typeinfo
<typeinfo>

⇒typeid演算子を使えるようにする


◆標準テンプレートライブラリ
Standard Template Library
STL

※アレクサンドル・ステパノフ氏らが考案
標準C++に組み込まれた。(標準C++規格ではSTLとは呼ばない)

コンテナライブラリは、反復子、アルゴリズム、ファンクタ(関数オブジェクト)
と組み合わせて利用する

_◇コンテナ
container

※他のオブジェクトを格納して情報の集まりを表す

※各種のデータ構造を含む
data structure

※列コンテナ
要素の並びが維持される
vector<> ベクトル
deque<> 両端キュー
list<> 双方向リスト
⇒コンテナアダプタ
│ stack<>
│ queue<>
│ priority_queue<>

※連装コンテナ
値のペア管理
│ map<>
│ multimap<> キーの重複を許す
│ set<> キーの集合
│ multiset<> キーの集合だが、重複を許す

※他のコンテナ
文字列 string
│ ⇒stringはtypedef名で、クラステンプレートはbasic_string<>
数値演算用配列 valarray<>
ビット集合 bitset<>

※C++第3版
array<>, unordered_sort<>, unorderd_multiset<>, unordered_map<>,
unorderd_multimap<>

_◇vector
標準ライブラリ vector<>

※もっとも基本的なコンテナ

①クラステンプレートとして実装
②要素の型は任意
③配列と同様に要素は記憶域に連続して格納される
(要素数、容量、先頭要素へのポインタなどをオブジェクトは格納し、
外部の連続した記憶域に実際のデータが1列に配置される)
④要素の増減が可能
⑤末尾への要素の追加、削除を高速に行える

※vectorテンプレート
要素型とアロケータ型の2つのテンプレート引数を受け取る
template <class T, class Allocator = allocator<T> >
class vector {

};
⇒アロケータ型引数は省略可能
アロケータは記憶域の動的な確保を、コンテナから独立して行う
標準ライブラリには allocatorという名の標準アロケータがあり、
省略するとこれが呼ばれる。

※publicなtypedef
size_type型
ベクトルの要素数や添え字に使う符号無し整数型
⇒実際にどのような整数型となるかは処理系依存
⇒ベクトルの要素や添え字はvector<>クラステンプレートのsize_type型で表す
例)
vector<int>::size_type
vector<double>::size_type
⇒上の両者は異なる型と認識される

例)
#include <vector>

宣言例)
vector<int> vt;
⇒要素型を<>内に指定する。但しコピー不可能な型は要素型とできない。
⇒オブジェクトの宣言時に要素数の指定は必要ない(要素数0)
⇒要素数を明示的に指定することもできる
│ 例) vector<int>vt2(10); //要素型のデフォルトコンストラクタで初期化される
│ //デフォルトコンストラクタを持たない型ではエラーとなる
│ 例) vector<int>vt3(10, 5);
│ //要素数10、初期値5で宣言
│ 例)既存の配列からの構築
│  vector<int>vt4(a, a+5);
│ //配列a[0]~a[4]の5個をコピーしてvectorを生成
│ //第1引数は先頭要素へのポインタ、第2要素は末尾のひとつ後方へのポインタ
⇒既存のベクトルオブジェクトからコピーコンストラクト
│ 例) vector<int> y(x1); //x1をコピーしてyを生成
⇒要素の挿入に伴って要素数が自動的に増加する
│ 論理的には要素を追加するたびに配列領域を確保しなおして全要素をコピー
│ 実際には、ある記憶容量を予め確保し、その範囲では再確保は行わない
⇒添え字演算子[]によって各要素にアクセスできる
│ 添え字演算子[]によるアクセスによって要素数が増加することはない
│ 不正添え字で例外は送出されず、実行時エラーが発生する
⇒メンバ関数size()によって要素数を調べられる
⇒以下の操作が使えるようになる

※代入
全要素を削除した上で、右辺の全要素を代入する

void push_front(const T& n)
│ 先頭にデータを追加する

void push_back(const T& n)
│ 末尾にデータを追加する

void pop_front()
│ 先頭からデータを取り出す

void pop_back()
│ 末尾からデータを取り出す。取り出した末尾要素は除去される

reverve(容量)関数
⇒記憶容量を予約する

capacity()関数
⇒確保済の記憶容量を返す

reference frount()
│ 先頭のデータへの参照を返す

reference back()
│ 末尾のデータへの参照を返す

iterator begin()
│ 先頭をさすイテレータを返す

iterator end()
│ 末尾をさすイテレータを返す

reference at(size_type i)
│ i番目のデータへの参照を返す
│ 不正なインデックスに対してout_of_range例外を送出する

iterator insert(iterator it, const T& x)
iterator insert(iterator it, size_type n, const T& x)
│ イテレータがさすデータの前にxを挿入する. nが指定されていればn個挿入する

void assign(size_type n, const T& u);
│ ベクトルの全要素を削除してn個の値uを挿入する

void clear()
│ 全てのデータを削除する

iterator erase(iterator it)
iterator erase(iterator itFirst, iterator itLast)
│ イテレータがさすデータを削除する。
│ firstとLastが指定されていた場合は、firstからLastの直前までを削除する

bool empty()
│ データが存在するか調べる

size_type size()
│ データ数を返す
⇒vector<要素型>::size_type型

※文字列の配列からベクトルを生成する例
const char s[] = “abcde”;
vector<char> sv(s, &s[strlen(s)]);

void resize(size_type sz, T c = T());
│ ベクトルの要素サイズをszに変更する。
│ 拡大する場合は、指定サイズになるまで末尾にcを追加
│ 縮小する場合は末尾から要素を削除
│ ⇒第二引数を明示的に指定すれば初期値を与えられる。

void swap(vector<T, Allocator>& t);
│ ベクトルの全要素をtの要素と入れ替える
│ x.swap(y)のようにして使うことも
│ swap(x,y)として使うこともできる
│ ⇒汎用のswapより、vector<>のswapの方がvector<>向けには早い

※等価演算子と関係演算子
2つのベクトルの等価性大小関係を判定する演算子
==, !=, <, <=, >, >=
非メンバ関数
⇒要素数同一で全要素の値が同一ならば==がなりたつ
⇒そうでない場合は先頭要素から順に比較、要素の値が一致しなかったときのその要素の大小関係でベクトルの大小関係が決まる
⇒要素数が小さくてもベクトル的には大きいと判定されることもある

※特殊化
vector<bool>
│ パックすることで記憶域を節約する

※iteratorクラス
一つずつデータを指し示すためのクラス
*は現在指し示されているデータを得るための演算子としてオーバーロードされている
++は、次のデータを指し示す演算子としてオーバロードされる。

例)

vector<int>::iterator it = vt.begin();
while (it != vt.end()) {
    cout << *it;
    cout << "-";
    it++;
}

_◇vectorの利用

※new演算子で配列を動的に生成して利用する場合の欠点
① delete[]演算子を呼び忘れると記憶域が開放されない。
② 不正な添え字によるアクセスに例外を適用できない
③ 要素数の増減に対応できない
⇒要素数が実行時にきまったり、変化する配列はvector<>で実装する

※2次元配列
vector<vector<int> > x(m, vector<int>(n));
などとすることにより、m行n列の2次元配列を動的に確保することができる。
⇒通常の2次元配列と異なり、行内の連続性は保たれるが、行が連続したアドレスに
配置されるとは限らない。

※ベクトル要素のアクセス
①添え字演算子[]
②メンバ関数at
③反復子(iterator)

_◇string

#include <string>

※stringの実体
クラステンプレートbasic_stringを明示的に特殊化したテンプレートクラス。<char>を型として与えたtypedef
⇒typedefにより<>型名は不要となる
⇒ワイド文字用のwstringは同じクラステンプレートに<wchar_t>を与えている
⇒よってstd::stringとstd::wstringの使い方は同じ
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;

※文字列リテラルはC言語形式のchar型文字列である

※std名前空間の中で定義されている

※C++11のstd:stringではUTF-8がサポートされる
⇒u8″xxx” のようにu8プリフィックスによりUTF-8リテラルとなる
⇒clang3.2以降は対応
⇒VC++ ~2013までは非対応

※basic_string<>内で定義されている主要な型と定数

size_type 文字列の長さや文字列内の場所を示す符号無し整数型

npos 文字列の長さや文字列内の場所としてあり得ない値を示す定数
│  size_type型の最大値
⇒nposの値は静的メンバ関数max_sizeで調べる

※初期化(コンストラクタ)
std::string str1; 初期値を与えないと””で初期化される。
std::string str2 = “xxx”;
⇒単一文字’x’による初期化は不可だが、代入はできる
│ str2 = ‘X’
std::string str3(“yyy”);
std::string str4(5, ‘a’); 5個の’a’で初期化

const char* const CSTR = “abcdefg”;
std::string str5(CSTR, 5); CSTRの先頭5文字で初期化
std::string str6(str5, 1); str5[1]以降の文字列
std::string str7(str5, 1, 3); str5[1]以降の3文字
⇒第2引数はstr5内を指さないとout_of_rangeとなる
⇒第3引数はstr5の大きさを越えても良い(負の値は大きな整数とみなされる)
std::string str8(&CSTR[3], &CSTR[6]); CSTR[3],[4],[5]の3文字

※イテレータ
std::stringのイテレータ型 std::string::iterator
⇒文字列の中の一つの文字を参照する
⇒begin()関数で先頭要素を参照するイテレータを取得できる。
⇒end()関数で最後の要素の一つ後ろを参照するイテレータを取得できる
⇒*演算子によりイテレータの参照先要素をアクセスできる。

例)

std::string str("abcde");

for (std::string::iterator it = str.begin();
    it != str.end(); ++it) {
        std::cout << *it << std::endl;
    }

※std::string型への代入
Cと異なりstrcpyを使わないでも代入できる。
str2 = str1;
⇒代入元はstd::string型だけでなく、char型配列 “abc” でも、文字’a’でもよい。
⇒assign関数によりコンストラクタと同様な書式で代入することもできる
例)
str2.assign(“xyzxyz”,4);
⇒std::stringは、内部で動的にメモリを割り当てるので事前に配列を確保しなくても良い。

※char型配列へのstd::string型の代入
copy関数を用いる。末尾にNULLの代入必要。
copy(代入先配列,最大文字数, [コピー開始位置])
⇒戻り値として実際にコピーされた文字数が返る。
⇒バッファオーバーフローの可能性がある。

例)
std::string str1(“abcde”);
char cstr[10];
std::size_t len;
len = str1.copy(cstr, sizeof(cstr) -1);
cstr[len] = ‘\0’;

※std::string型への添え字アクセス
⇒char型の配列的に使用できる。
例)
std::string str1(“abcde”);
char c;
c = str1[2];

※at関数による添え字アクセス
⇒at関数に添え字を与える
⇒at関数は範囲外アクセスを検出し、out_of_range例外を投げる
例)

try {
    c = str1.at(5)
}
catch (std::out_of_range) {
    std::cerr << "out of range " << std::endl;
    return -1;
}

※文字列連結
C言語形式のchar型配列と異なりstrcatを使わないでも連結できる。
⇒ += + 演算子
str2 += str1;
str2 += “abc”;
str3 = str1 + str2;
⇒char型配列同士を + 演算子でつなぐとポインタとポインタの加算でエラーとなる
⇒+演算子は左結合なので、左にくるものがstring型であれば右がchar型配列でも結合可能、逆は不可。
⇒”abc” + ‘D’はchar型配列へのポインタに対する整数加算なのでポインタになるので注意。
⇒+=演算子は左にくるものがstring型であれば、右はstring, char型文字列、charのいずれでもよい。
⇒append関数による連結もできる
str1.append(“xyz”, 2);
⇒引数はコンストラクタ類似のバリエーションあり
⇒1文字だけの連結にはpush_back()関数も使える
str1.push_back(‘x’);

※文字列挿入
文字列の途中に他の文字列や文字を挿入する場合は insert()関数
str1.insert(1, “x”); str[1]の直後に”x”挿入
str2.insert(1, str2);

※サイズ
文字列の長さは size()関数またはlength()関数による
str.size()
str.length()
⇒戻り値はstd::string::size_type型
⇒可能性のある上限値をmax_size()関数で取得可能
⇒サイズ0のチェックはempty関数
str.empty()

※サイズの変更
resize(文字数,[空文字])関数。
⇒指定したサイズになるように文字列を書き換える。大きくする場合は、空文字で埋める。小さくなる場合は余分な文字列をカットする。

※実容量
std::stringは実サイズより大きめにメモリを確保することで、効率化を図っている
実サイズは capacity()関数で取得できる。
⇒reserve()関数によりメモリを確保することもできる。(指定した値以上となる)

※比較
通常の比較演算子が使える。比較対象はchar型配列でも受け付ける
==
!=
<
<=
>
>=
⇒compare関数により、負、0、正の値を返すこともできる
例)
str1 = “abc”;
if (str1.compare(“xxx”) …

※検索
find 指定文字、文字列の最初の出現位置
rfind 指定文字、文字列の最後の出現位置
find_first_of 指定文字、文字列の一部の文字の最初の出現位置
find_last_of 指定文字、文字列の一部の文字の最後の出現位置
find_first_not_of 指定文字、文字列の一部でない文字の最初の出現位置
find_last_not_of 指定文字、文字列の一部でない文字の最後の出現位置
⇒発見できないときは、std::string::npos値を返す。
これは std::string:size_type型の-1
⇒戻り値を一旦変数に受ける場合は、std:string::size_type型変数で受ける。
⇒ここでnposと比較した後で、問題なければintにしてもよい。
例)
string::size_type pos = txt.find(pat);

※置換
replace()関数
例)
s1.replace(pos, s2.length(), s3);

※削除(空文字列にもどす)
clear()関数、erase()関数

※部分文字列
substr()関数

※strlen()
文字列の中の文字の数を戻す

※strupr()
文字列を大文字に変換する

※strlwr()
文字列を小文字に変換する

_◇コンテナとしてのstring

※コンテナの一種であるので、以下のメンバ関数、演算子が使える
at 不正な添え字でout_of_rangeエラーを送出する
capacity
clear
empty
erase
max_size
resize
size
swap
[] 不正な添え字がエラーとならない

※内部表現
①C言語式のchar型文字列 末尾のNULL文字が終端
②stringは動的に確保された領域に文字列が格納される。NULL文字はない。
確保したサイズはcapacity()で、実際に使っている要素数はsize()またはlength()で調べられる。reserve()で容量予約、resize()で容量を指定することができる。

※反復子によるアクセスが可能
⇒文字列内の文字

_◇C言語形式文字列とstringの相互変換

C形式=>string型変換
明示的にも暗黙的にもおこなうことができる

string型⇒C形式
c_strメンバ関数による
c_strメンバ関数は、ナル文字を付加した文字列へのポインタを返すが、元のstringに対して、非constのメンバ関数を呼びだすと元の文字列そのものが書き換わってしまう。
※c_strの返すポインタをstrcpyで別の領域にコピーしてから使うのが良い。

※ファイルをオープンする際に指定するファイル名はC形式の文字列が必要

※std::string型文字列のconst char*型への変換
⇒c_str()関数による。
末尾にNULLが付加される。ただし、std::string型をポイントしているポインタを作るだけなので、std:string型の文字列に変化が生じると無効となる。
std::string str(“abc”);
const char* s = str.c_str();

※コマンドライン引数の第二引数
C言語形式文字列をそれぞれ指す、ポインタの配列
⇒文字列の配置の順序や連続性は保証されない
⇒stringのvector型にするには要変換

※2次元C言語形式文字列配列
⇒すべての要素は連続配置

_◇string型への入出力

※ストリーム入出力
抽出子 >> と 挿入子 << は、<string>ヘッダで定義されている

※抽出子 >> によりstring型に読み込むと、文字列の格納領域は自動的に調整される
⇒C形式の char s[6] などでは、定義した文字数を超えることはできない

※抽出子 >> による文字列読み込みでは、スペースやタブなど空白文字は区切り記号となり、読み飛ばされる。

※スペースを含めた文字列の読み込み
getline(cin, 変数名)
Enterキーを押すまでの全文字が変数に格納される
⇒区切り文字である開業文字は文字列には格納されない
⇒任意の文字を区切り文字とするgetlineも多重定義されている

_◇list
双方向に移動できるデータ構造

_◇deque.h
先頭・末尾からアクセスできるデータ構造

_◇stack
後入先出型のデータ構造

_◇queue
先入先出型のデータ構造

_◇set
重複のない集合を管理するデータ構造

_◇map
データと検索キーを管理するデータ構造

_◇algorithm
汎用的なアルゴリズムが含まれる

TI 位置を示す型
T 値の型

difference_type count(TI first, TI last, const T& n)
│ カウントする

TI find(TI first, TI last, const T& n)
│ 検索する

void reverse(TI first, TI last)
│ 逆順にする

void replace(TI first, TI last, const T& n1, cont T& n2)
│ 置換する

TI remove(TI first, TI last, cont T& n)
│ 除去する

TI3 merge(TI1 first1, TI1 last1, TI2 first1, TI2 last2, TI3 i)
│ 連結する

void sort(TI first, TI last)
│ ソートする

TI1 search(TI1 first1, TI1 last1, TI2 first2, TI2 last2)
│ 探索する

※関数テンプレート swap
⇒第1、2版の標準C++では<algorithm>ヘッダに含まれる

random_shuffle(RandomAccessiterator first, RandomAccessitereter last);
│ 先頭要素と末尾要素の間をランダムにシャッフルする

※C++14ではstd::shuffle()関数に置き換わった
⇒random_shuffleは内部でrand(大域な記憶を使ってしまう)を呼んでいるため
⇒shuffle()関数は第3引数に乱数ジェネレータを取る

sort(RandomAccessiterator first, RandomAccessitereter last);
│ 昇順ソート

sort(RandomAccessiterator first, RandomAccessitereter last, greater<型>());
│ 降順ソート

greater<型>()はfunctionalヘッダで定義されているプレディケート

※for_each
template <class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f);
⇒firstからlast-1の要素を順に走査しながらその各要素にファンクタfを適用する

_◇utility
<utility>

※関数テンプレート swap
⇒第3版の標準C++では<utility>ヘッダに含まれる

_◇limits

※numeric_limitsクラステンプレート
⇒処理系での基本型の諸特性の情報を提供する
⇒min, maxで最大、最小が表される

※整数型のビット数
const int型の静的データメンバdigitsとして提供される
⇒digitsは符号以外のビット数なので、unsigined型に対してビット数の実体を示す。

※浮動小数点型のdigitsは誤差なしで表現可能なradix進法での仮数部の桁数

例)
std::numeric_limits<unsigned char>::digits
⇒unsigned char型の構成ビット数

_◇sstream
文字列ストリーム
#include <sstream>

※種々の型を<<演算子を使って文字列に変換できる。

※非メンバ関数として定義する挿入子<<が多相的に振る舞う様にするためには、
表示内容を文字列として返却する純粋仮想関数の返却値を出力するように定義する。

例)

class 基底クラス {
    virtual std::string to_string() const = 0;
};

class 派生クラス {
std::string to_string() const {
    std::ostringstream os;
    os << "Rectangle(width:" << width << ", height:" << height << ")";    return os.str();
    }
};

inline std::ostream& operator<<(std::ostream& os, const 基底クラス& s)
{
    return os << s.to_string();
}

_◇functional

比較、論理演算を行う標準関数オブジェクト、ファンクタ(functor)が定義されている

not_equal_to x != y
equal_to x == y

less x < y
greater x > y
less_equal x <= y
greater_equal x >= y
logical_or x || y
logical_and x && y

logical_not !x

算術演算を行うファンクタ
plus x + y
minus x – y
multiplies x * y
divides x / y
modulus x % y

negate -x


☆処理系限界


◆処理系限界の推奨値

1論理ソース行文字数 65536
文字列リテラル連結後文字数 65536
1関数定義内仮引数個数 256
1関数呼び出し実引数個数 256
atexit関数登録可能数 32
1オブジェクトの大きさ 262144
1コンストラクタ内メンバ初期化子数 6144
1クラス内アクセス制御宣言数 4096
1メンバ構造体内データメンバ数 16384
1クラス内静的メンバ数 1024
1つのクラスの直接基底クラス数 1024
1つのクラス内メンバ数 4096
1つのクラスの直接仮想基底クラス+間接基底クラス数 1024
1つのクラスの最終オーバライド仮想関数の個数 16384
1つのクラス内のフレンド関数の個数 4096
直接基底クラス+間接基底クラスの合計数 16384
1テンプレート宣言内のテンプレート実引数個数 1024
テンプレート具現化の再帰的ネスト段数 17
1つのtryブロックのハンドラ個数 256
一つの関数宣言に対する割り込み送出指定数 256


☆コーディングポリシー


◆ヘッダ

_◇非テンプレートヘッダ

※複数の*.cppファイルで共通に使用される「何か」を記述する
①マクロの定義
│ #define
②クラスの定義
│ class
③変数宣言(定義ではない)
│ extern
④関数のプロトタイプ宣言
⑤他のヘッダのインクルード
│ #include

※複数ファイルに何度かインクルードされるので
⇒メモリ領域を確保するようなコードは入れてはならない
①変数の定義 int など
│ ⇒変数の実体はどこか一つのコードに置く
②関数の実体の定義

_◇テンプレートヘッダ

テンプレートを含むヘッダファイル
⇒テンプレートクラスの定義はヘッダの中におく
⇒コンパイラは、テンプレートの定義をすべて読んだ後に、テンプレート引数に対応するクラス定義を改めて生成する

※ヘッダファイル同士が呼び合う必要がない

_◇ワンタイムインクルード
同じインクルードファイルを複数回インクルードすると、重複定義を引き起こす
⇒ワンタイムインクルード(インクルードガード)

#ifndef 固有の文字列
#define 固有の文字列
    ヘッダの実体
#endif

あるいは
#if !defined ___XXX
#define ___XXX

#endif

※C++ではアンダーライン文字2個で始まる名前が予約されている
⇒アンダーライン3個を続けた名前を識別用のマクロ名(文字列)とするとよい

※VC++
│ ヘッダ冒頭に
│ #pragma onece
│ でもワンタイムインクルードとなる

_◇ヘッダファイルでの名前空間

※ヘッダ部には using指令をおかない
│ ⇒using指令をヘッダに記述するとヘッダをインクルードするソースでusingが勝手に有効になってしまう

※std名前空間も省略できない
│ std::string
│ std::cout
│ のように明示的に記述する

_◇インクルードモデル

※テンプレートクラス
│ ⇒ヘッダ部とソース部に分けて実現するのは実用的でない
│ ⇒クラステンプレートが大きくなると単一のヘッダでの管理が困難になる

※インクルードモデル
include model
│ ⇒主としてクラス定義を含むクラステンプレートのヘッダ部と、クラス定義の外で行うメンバ関数の定義を分け、2つのヘッダにする方法

①XXX.h

#ifndex ___Class_XXX
#define ___Class_XXX

template <class Type> class XXX {
    ...
public:
    int func(double x);
};

#include "XXX_Implementation.h"

#endif

②XXX_Implementation.h

#ifndef ___Class_XXX_Implemenation
#define ___Class_XXX_Implementation

//メンバ関数の定義
template <class Type>
int XXX<Type>::func(double x)
{
    ...
};

#endif


◆差分プログラミング
incremental programming


◆ポリモーフィズム
多相性