|
C++の基礎知識EX
【番外編 (予備的な事項)】
| ●関数テンプレート | ●クラステンプレート | ●プロプロセッサ | ●コンソール出力 | ●キーボード入力 |
通常の関数では、データ型とそれに対する処理コードはセットで扱いますが、関数テンプレートを使うと、同じ処理コードで異なるデータ型を扱うことができます。書式は次のようになります。型の一時名のところには、定義の際は任意の名前を設定をします。関数内では、この一時名を未定のデータ型として使用することができます。そして関数を使用する際に、既存のデータ型名を記述します。
template <typename 型の一時名> 関数の型 関数名(引数1, 引数2, ...) { ... }
呼出 : 関数名<適用するデータ型>(引数);
// 関数テンプレート : 二つの値を比較して結果値(-1, 0, 1)を返す
template <typename _t> int CompareValue(_t value1, _t value2) {
if (value1 > value2) return 1; // value1が大きいときは 1 を返す
if (value1 < value2) return -1; // value2が大きいときは -1 を返す
return 0; // 同じか比較不能の時は 0 を返す
}
// main関数
void main() {
int n1 = CompareValue<int>(11, 99); // = -1 (int型の比較)
int n2 = CompareValue<double>(9.9, 1.1); // = 1 (doublu型の比較)
int n3 = CompareValue<char>('x', 'x'); // = 0 (char型の比較)
}
クラステンプレートを使うと、同一処理のロジックで異なるデータ型を扱うことができるようになります。書式は以下の通りとなります。型の一時名のところには、定義の際は任意の名前を設定をします。クラス内では、この一時名を未定のデータ型として使用することができます。そして実際にクラスを使用する際に、既存のデータ型名を記述します。
template <typename 型の一時名, ...> class クラス名 { ... };
宣言 : クラス名<適用するデータ型> 変数;
// クラステンプレート : 型未定の配列をメンバに持つクラス
template <typename _t> class Collection {
private:
_t array[10]; // メンバ変数 : 型未定の10個の配列
public:
Collection(){} // コンストラクタ
~Collection(){} // デストラクタ
void SetItem(int index, _t value); // メンバ関数 : 値の設定
} ;
// メンバ関数の実装 : 指定したインデックスの要素の値を設定
template <typename _t> void Collection<_t>::SetItem(int index, _t value) {
array[index] = value;
}
// main関数
void main() {
int i ;
Collection<int> c1; // int型を指定して宣言
Collection<double> c2; // double型を指定して宣言
for (i=0; i<10; i++) {
c1.SetItem(i, i); // 10個の配列に 0, 1, ...9 を代入
c2.SetItem(i, i * 0.1) ; // 10個の配列に 0.0, 0.1, ...0.9 を代入
}
}
[#include] プリプロセッサはプログラム実行の前段階の処理を行います。#include は、ヘッダファイルやリソースファイルなど、他のファイルを取り込むためのコマンド(命令)です。この時、既存のヘッダファイルを取り込む時は、< > でファイル名を囲み、自作のファイルを取り込む時は " " で囲みます。 また、古いヘッダファイルや、逆に新しいヘッダファイルを取り込む時は、拡張子 ".h" を省き、代わりに名前空間(広範囲の定義領域)を使用する宣言 (using namespace) を行います。通常、既定の名前空間は std (=スタンダード) なので、"using namespace std; " を記述します。この仕様は、コンパイラとバージョンによって異なります。(名前空間は自分で定義することも可能です。)
// ヘッダファイルの取り込み
#include <stdio.h>
#include <stdlib.h>
#include "myheader.h"
| // ヘッダファイルの取り込み
#include <iostream>
#include <string>
using namespace std;
|
[#define, #undef] #define は文字列の置換えを行います。定数定義に使用される他、マクロ定義にも用いられ、プログラムの命令文として代用が可能です。ただし、あくまでも文字列の置換えなので、演算子の優先順位などの自動判断は行われません。定義の際には括弧などを多用して気を配る必要があります。 この時、複数行にわたる記述を行いたい時は、記号 \ を挿入して行の区切りとすることができます。また、一度定義したマクロを取り消したい時は、#undef を用います。
// マクロ定義
#define func(a, b) a * b
int n = func(3+2, 6+4);
// → 3+2*6+4 = 19
...
// これを防ぐには()を付ける
#define func(a, b) (a)*(b)
int n = func(3+2, 6+4);
// → (3+2)*(6+4) = 50
|
// #define と #undef
#define COUNTER 300 // 定数定義
#undef COUNTER // 未定義化
#define COUNTER 500 // 再定義
...
...
// 複数行の記述
#define func(a, b, c, d) \
((a)*(b))-((c)*(d))
|
[#if, #ifdef, #ifndef] #if は条件付コンパイルをしたいときに用います。判断するのは整数に限られます。 また、#ifdef (または #if defined) は、ある識別氏が定義済みであるかどうかを判断します。#ifndef (または #if !defined) は逆に、ある識別氏が定義されていないかどうかを判断するためのものです。ヘッダファイルを重複して取り込まないようにするためには通常これを用います。 なお、いずれも分岐の数に応じて、#elif, #esle を用いて判断式を追加することができます。
// 条件付コンパイル
#defined MEM_CAP 512
#if MEM_CAP>=512
char book[800][100];
#elif MEM_CAP>=256
char book[600][100];
#else
char book[500][100];
#endif
| // ヘッダファイルのチェック
#ifndef _CODE_H_DEFINED
#define _CODE_H_DEFINED
#include "code.h"
...
(ヘッダファイルの中身の記述)
...
#endif
|
これらは論理演算子も使用することができます。
#if AAA && BBB, #if CCC || DDD
#if defined(AAA) && defined(BBB), if !defined(CCC) || !defined(DDD)
|
コンソール画面への出力には、printf, cout などを使用します。printf は書式付で出力するための関数です。この関数を使用するには stdio.h の取り込みが必要です。(下表は主な機能の抜粋です)
printf("書式", 値1, 値2, ...);
書式:"% フラグ 文字数 精度 型指定 変換形式"(%と変換形式以外は省略可)
*書式に現れた変換文字(%...)に値1, 値2... を順番に代入して表示する。
| 種類 | 記述 | 機能 |
| フラグ | - | 左詰め(省略時は右詰め) |
| + | '+' '-' 符号を表示 |
| (空白) | 正の数値の先頭に空白を表示 |
| 0 | 0で桁を埋める |
| # | 出力形式の変換
・変換形式が o の時 : 先頭に 0 を付加
・変換形式が x の時 : 先頭に 0x を付加
・変換形式が f, e の時 : 小数点をつける |
| 文字数 | 数値 | 文字数 |
| * | 引数で指定された文字数 |
| 精度 | .数値 | 小数点以下の桁数
・変換形式が d,i,o,u,x の時 : 最低でも数値分の数字を置く
・変換形式が f, e の時 :小数点以下に数値分の数字を置く
・変換形式が s の時 : 最大表示文字数 |
| . ( .0) | 小数を非表示 |
| .* | 引数で指定された精度 |
| 型指定 | h | (unsigned) short型 (変換が p の時はポインタ型) |
| l | (unsigned) long型 (変換が p の時はポインタ型) |
| 変換形式 | d, i | 十進値 |
| o | 八進値 |
| x (X) | 十六進値 |
| u | 符号なし十進値 |
| f | 浮動小数点(float, double) |
| e (E) | 指数値 |
| p | ポインタ(アドレス) |
| c | 文字(char) |
| s | 文字列 |
| % | '%' 自身 |
#include <conio.h>
#include <stdio.h>
int main() {
int a = 77;
int b = 88;
printf("%d, %d\n", a, b); // "77, 88"
printf("%#x\n", a); // "0x4d"
printf("%04d\n", b); // "0088"
if (getch()) return 0;
}
| #include <conio.h>
#include <stdio.h>
int main() {
double a = 7.7;
printf("%f\n", a); // "7.700000"
printf("%.3f\n", a); // "7.700"
printf("%+.f\n", a); // "+8"
printf("%.*f\n", 4, a); // "7.7000"
if (getch()) return 0;
}
|
printf の他にも、標準出力を表す cout があります。 "cout <<" を利用すると、データ型の指定なしに出力することができます。この機能を使用するときは、iostream.h の取り込みが必要です。また、以下のような記述(マニピュレータ)を追加して書式を指定することもできます。(抜粋)
| 記述 | 機能 | 例 |
| dec | 十進値 | cout << dec << 100; |
| oct | 八進値 | cout << oct << 100; |
| hex | 十六進値 | cout << hex << 100; |
| fixed | 固定小数点形式(浮動小数点) | cout << fixed << 9.9; |
| scientific | 指数形式(浮動小数点) | cout << scientific << 9.9; |
| showbase | 0(8進)または0x(16進)を付加 | cout << showbase; |
| noshowbase | showbase をクリア | cout << noshowbase; |
| endl | 改行(\n)の挿入 | cout << 100 << endl; |
| ends | ヌル文字(\0)の挿入 | cout << 'a' << 'b' << ends; |
#include <conio.h>
#include <iostream.h>
// または、
// #include <iostream>
// usingnamespace std;
int main() {
char s1[10] = "Hello ";
char s2[10] = "World !";
cout << s1 << s2 << endl;
if (getch()) return 0;
}
| #include <conio.h>
#include <iostream.h>
int main() {
int n = 100;
cout << showbase;
cout << n << endl; // 100
cout << oct << n << endl; // 0144
cout << hex << n << endl; // 0x64
if (getch()) return 0;
}
|
キーボードからの入力を受け付ける方法には、標準入力を表す cin と文字列を変数に格納する getline関数を利用する方法があります。getline関数では、EOF(末尾 : End Of File)、または指定した区切り文字(省略時は改行 '\n' )までを変数に格納します。 さらに、予期せぬ入力(文字数オーバーなど)によるエラーを防ぐため、格納する変数には可変長文字列 string型を指定して安全性を確保する必要があります。 なお、string型と getline関数を使用するには string.h、cinを使用するには iostream.h の取り込みが必要になります。
getline(cin, string型変数, 区切り文字(通常は改行文字 '\n'));
* 標準入力(cin)を区切り文字('\n')まで読み込んで変数に格納
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <iostream.h>
// または、
// #include <string>
// #include <iostream>
// using namespace std;
int main()
{
string s; // string型変数
printf("入力してください : "); // 入力の要請
// 標準入力(cin)を末尾か'\n'までを sに代入
getline(cin, s, '\n');
// 画面出力
// printfの場合 c_str()で、
// Cスタイルの文字列に変換する
printf("格納した文字列 : %s\n", s.c_str());
// または cout << s << endl;
if (getch()) return 0;
}
|
// getc(stdin) を利用(getchar()でも可)
#include <stdio.h>
#include <conio.h>
void main() {
const int len = 16; // 格納する文字数
char buf[len]; // 格納する変数
char c; // 検知した文字
int i = 0; // カウンタ変数
// ユーザーに入力を要請
printf( "入力してください : " );
// 標準入力(stdin)から
// 一文字づつcに代入して
// 末尾(EOF)か改行(\n)までループ
while ((c=getc(stdin))!=EOF && c !='\n') {
// 文字を格納(-1はヌル文字分)
if (i < len-1) buf[i++] = c;
}
buf[i] = '\0'; // 末尾にヌル文字を追加
printf("格納した文字列 : %s\n", buf);
if (getch()) return;
}
|
また、 getc関数や getchar関数を利用して、一文字づつ取得しながら、変数に格納していく方法もあります。上記右例は、読み取る文字数が決まっている時の入力受付の一例です。この方法では、ユーザーが何文字入力しても決まった文字数しか受け取りません。なお、getc や getchar 関数を使用するには、stdio.h の取り込みが必要です。
|