C++/標準テンプレートライブラリ

出典: フリー教科書『ウィキブックス(Wikibooks)』
< C++
ナビゲーションに移動 検索に移動

はじめに[編集]

標準テンプレートライブラリ (英: Standard Template Library、STL) は、言語を問わずあらゆる場面でよく用いられるデータ構造等を提供することを目的として導入されました。STLの中には、連想配列リストなどのデータ構造が定義されています。これらは、Perlや、Pythonなどの言語では標準のデータ構造として言語に組み込まれています。これらのデータ構造は、Cのプログラムでもよく用いられます。しかし、Cはこのようなデータ構造を言語自身の機能として持ってはいなかったため、Cを用いるプログラマは、プログラムを作成するときに、まず用いるデータ構造を作成する必要がありました。例えば、Apache httpdや、glibでは、STLで定義されるようなデータ構造が作成されています。 STLは作業の重複を省く意味でも積極的に用いるのがよいでしょう。ただし、これらのデータ構造を用いることだけが目的なら、PerlPythonなどを試してみることを勧めます。これらの言語はC++ほど速く実行されはしません。しかし、C++と比べて比較的簡潔にこれらを用いることが出来ます。そのため、STLはC++を用いる上でこれらのデータ構造が必要になった時に使われます。

文字列[編集]

Cではchar型のポインタ (以下、C形式の文字列) を用いる事で文字列を操作します。 以下の例を見てください。

例1: C形式の文字列を操作する

 #include <stdio.h>
 #include <string.h>
 
 int main()
 {
   const char * s1 = "mojiretu";
   char s2[256];
 
   strcpy(s2, s1);    /* 文字列のコピー */
   printf("%s\n", s2);
   strcat(s2, s1);    /* 文字列の追加 */
   printf("%s\n", s2);
   printf("%zu\n", strlen(s2));    /* 文字列のサイズ */
 
   return 0;
 }

C++では、C形式の文字列の代わりにstd::string (以下、単にstring) 型を用いて文字列操作を行うことができます。 string型を使って上記のコードと同じ働きをするコードを書くと、次のようになります。

例2: 例1と同じ動作をするコード

 #include <iostream>
 #include <string>
 
 int main()
 {
   std::string s1 = "mojiretu";
   std::string s2;
 
   s2 = s1;    /* 文字列のコピー */
   std::cout << s2 << std::endl;
   s2 += s1;    /* 文字列の追加 */
   std::cout << s2 << std::endl;
   std::cout << s2.size() << std::endl;    /* 文字列のサイズ */
 
   return 0;
 }

string型にはC形式の文字列を使うのにくらべて、次のような違いがあります。

  • 事前に文字列の最大数を指定する必要がなく、適宜メモリサイズが拡張される。
  • コピーや追加等の操作を演算子により行える。
  • 現在の文字列のサイズを、オブジェクトの中に持っている。

ところで、例1を関数に分割すると、次のようになります。

例3: 例1を関数に分割したバージョン

#include <stdio.h>
#include <string.h>

void func(const char* s1)
{
  char s2[256]; //<--s1の文字列サイズチェックがないとs2にコピー時オーバーフロー発生する可能性ある

  strcpy(s2, s1);    /* 文字列のコピー */
  printf("%s\n", s2);
  strcat(s2, s1);    /* 文字列の追加 */
  printf("%s\n", s2);
  printf("%zu\n", std::strlen(s2));    /* 文字列のサイズ */
}

int main()
{
  char s1[] = "mojiretu";

  func(s1);

  return 0;
}

例3ではfunc()main()からしか呼ばれません。 しかし、func()に渡された文字列の長さをチェックしないと、s2に対して範囲外へのアクセスを行う可能性があるため危険です[注 1]

これに対してC++バージョンを関数分割すると、 次のようになります。

例4: 例2を関数に分割したバージョン

#include <iostream>
#include <string>

void func(const std::string& s1)
{
  std::string s2;

  s2 = s1;    /* 文字列のコピー */
  std::cout << s2 << std::endl;
  s2 += s1;    /* 文字列の追加 */
  std::cout << s2 << std::endl;
  std::cout << s2.size() << std::endl;    /* 文字列のサイズ */
}

int main()
{
  std::string s1 = "mojiretu";

  func(s1);

  return 0;
}

string型は必要に応じて内部のバッファが拡張されるので、引数で渡された文字列のサイズを気にせずコピーや追加を行うことが出来ます。

注釈[編集]

  1. ^ 257文字以上の文字列をfunc()に渡すとバグの原因になります