C,C++,VCに関する雑多なこと
C,C++†
出力オペレータ†
vectorや自分で作ったクラスに対して画面出力オペレータを定義する.
以下はvectorに関してオペレータを作成した場合の例.
1
2
3
4
5
6
7
8
9
10
| | template<class T>
inline ostream &operator<<(ostream &out, const vector<T> &x)
{
vector<T>::const_iterator i = x.begin();
for(; i != x.end(); ++i){
out << *i << " ";
}
return out;
}
|
Win32環境でのマルチスレッド†
Win32でのスレッド生成には,
- _beginthread
- _beginthreadex
- CreateThread
などがある.CreateThread はメモリリークがあるらしい.
_beginthreadが一番簡単で,スレッドハンドルも_endthreadで閉じてくれる.
_beginthreadexが一番安全なもよう.ただし,スレッドハンドルはCloseHandleで手動で閉じる必要がある.
以下は_beginthreadexを用いたもっとも単純な例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| | #include <process.h>
#include <Windows.h>
unsigned int func(void *x)
{
for(int i = 0; i < 10; ++i){
cout << "thread " << *((int*)(x)) << endl;
}
_endthreadex(0);
return 0;
}
int main(void)
{
HANDLE handle;
int x = 1;
handle = (HANDLE)_beginthreadex(NULL, 0, (unsigned int (__stdcall*)(void*))func, &x, 0, NULL);
for(int i = 0; i < 10; ++i){
cout << "thread 0" << endl;
}
WaitForSingleObject(handle, INFINITE);
CloseHandle(handle);
return 0;
}
|
メンバ関数ポインタ†
クラスのメンバ関数を関数ポインタを使って扱う方法.
例えば,以下のようなクラスがあるとする.
1
2
3
4
5
6
| | class FuncPtrTest
{
public:
void Func1(int i){ cout << i << endl; }
void Func2(int i){ cout << 2*i << endl; }
};
|
メンバ関数ポインタ変数は,
void (FuncPtrTest::*pFunc)(int) = &FuncPtrTest::Func1;
のようにしてとることができる.
typedefで関数を定義する場合は以下.
typedef void (FuncPtrTest::*FUNC)(int);
オペレータ"->*",".*"を用いた呼び出し†
呼び出す場合は,
FuncPtrTest *pFuncClass = new FuncPtrTest;
(pFuncClass->*pFunc)(1);
となる.ポインタでないクラスオブジェクトを用いる場合は以下のようになる.
FuncPtrTest funcClass;
(funcClass.*pFunc)(1);
typedefした場合は以下.
FUNC defFunc = &FuncPtrTest::Func2;
(pFuncClass->*defFunc)(8);
メンバ関数内から呼び出す場合はthisポインタを用いる.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| | class FuncPtrTest
{
public:
void Func1(int i){ cout << i << endl; }
void Func2(int i){ cout << 2*i << endl; }
void Test(void (FuncPtrTest::*func)(int));
};
void FuncPtrTest::Test(void (FuncPtrTest::*func)(int))
{
(this->*func)(2);
}
void main(void)
{
void (FuncPtrTest::*pFunc)(int) = &FuncPtrTest::Func2;
FuncPtrTest *pFuncClass = new FuncPtrTest;
pFuncClass->Test(pFunc);
delete pFuncClass;
}
|
テンプレートを用いた呼び出し†
テンプレート関数を用いることでもクラスオブジェクトを指定することができる.
1
2
3
4
5
6
7
8
9
10
11
| | template<FuncPtrTest* P>
void FuncT(int i)
{
P->Func1(i);
}
FuncPtrTest g_FuncClass;
void main(void)
{
FuncT<&g_FuncClass>(8);
}
|
ただし,テンプレートに指定するクラスオブジェクトはグローバル変数でなければならない.
boost::bindを用いた呼び出し†
boost::bindを用いることで,通常の関数ポインタとメンバ関数ポインタを
区別なくboost::functionとして扱うことができる.
まず,boost::functionを用いて関数を定義する.
#include <boost/bind.hpp>
#include <boost/function.hpp>
void TestBind(boost::function<void (int)> func)
{
func(16);
}
関数の呼び出しでは,bindを用いてクラスオブジェクトを指定する.
FuncPtrTest *pFuncClass = new FuncPtrTest;
TestBind(boost::bind(&FuncPtrTest::Func2, boost::ref(pFuncClass), _1));
コード例†
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
| | #include <boost/bind.hpp>
#include <boost/function.hpp>
class FuncPtrTest;
typedef void (FuncPtrTest::*FUNC)(int);
class FuncPtrTest
{
public:
void Func1(int i){ cout << i << endl; }
void Func2(int i){ cout << 2*i << endl; }
void Test(void (FuncPtrTest::*func)(int));
};
void FuncPtrTest::Test(void (FuncPtrTest::*func)(int))
{
(this->*func)(2);
}
template<FuncPtrTest* P>
void FuncT(int i)
{
P->Func1(i);
}
void TestBind(boost::function<void (int)> func)
{
func(16);
}
FuncPtrTest g_FuncClass;
void main(void)
{
FuncPtrTest *pFuncClass = new FuncPtrTest;
void (FuncPtrTest::*pFunc)(int) = &FuncPtrTest::Func1;
(pFuncClass->*pFunc)(1);
pFunc = &FuncPtrTest::Func2;
(pFuncClass->*pFunc)(1);
pFuncClass->Test(pFunc);
FuncT<&g_FuncClass>(8);
FUNC defFunc = &FuncPtrTest::Func2;
(pFuncClass->*defFunc)(8);
TestBind(boost::bind(&FuncPtrTest::Func2, boost::ref(pFuncClass), _1));
delete pFuncClass;
}
|
実行結果
1
2
4
8
16
32
ビット演算†
演算の種類 | 演算子 | 使用例 |
論理積(AND) | & | x & y, x &= 0x01 |
論理和(OR) | | | x | y, x |= 0x01 |
否定(NOT) | ~ | y = ~x; |
排他的論理和(XOR) | ^ | y ^ x; y ^= x; |
右シフト | >> | y = x >> 2; |
左シフト | << | y = x << 2; |
実行例
1011&0101 = 0001
1011|0101 = 1111
~1011 = 0100
1011^0101 = 1110
1011>>2 = 0010
1011<<2 = 1100
メンバ関数テンプレート†
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| | class rxTemplateTest2
{
Type data;
public:
rxTemplateTest2();
~rxTemplateTest2(){};
template<typename T>
void InlineFunc(Type x)
{
data = x;
}
template<typename T>
void Func(Type y);
};
template<typename Type>
void rxTemplateTest2::Func(Type y)
{
data = y;
cout << data << endl;
}
|
テンプレートクラス†
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| | template<class Type>
class rxTemplateTest
{
Type data;
public:
rxTemplateTest();
~rxTemplateTest(){};
void InlineFunc(Type x)
{
data = x;
}
void Func(Type y);
};
template<class Type>
rxTemplateTest<Type>::rxTemplateTest()
{
data = 0;
}
template<class Type>
void rxTemplateTest<Type>::Func(Type y)
{
data = y;
cout << data << endl;
}
|
Visual C++だとテンプレートクラスの実装をcppファイルに書くと
テンプレートクラスを実体化できない設計になっている(inclusion-model)ので,
基本的にはヘッダにすべての実装を書くこと.
どうしてもcppファイルに書きたい場合は,テンプレートクラスの実体を明示する(明示的実体化).
上記の例だと,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| | #include "template_test.h"
template<class Type>
rxTemplateTest<Type>::rxTemplateTest()
{
data = 0;
}
template<class Type>
void rxTemplateTest<Type>::Func(Type y)
{
data = y;
cout << data << endl;
}
template class rxTemplateTest<int>;
template class rxTemplateTest<float>;
|
プリプロセッサ†
コンパイル前にソースに対して行われる前処理のこと.
C言語のプリプロセッサ命令(ディレクティブ)には"#"が頭に付く.
#include†
ヘッダファイルの読み込み
#include <stdio.h>
#include "vec.h"
基本的にはシステムで用意されているヘッダは<>で囲み,
自分で作成したヘッダを""で囲む.
#define†
マクロ置換.数値や文字列などの定数として扱える.
#define 定数名 置換後の数値や文字列
例えば,
#define PI 3.14159265358979323846
#define GRAVITY 9.80665
#define FILENAME "test.dat"
マクロ関数として関数も定義できる.
#define AREA(r) (r*r*PI)
#define MAX(a, b) ((a > b) ? a : b)
#define FEQ(a, b) (fabs(a-b) < 1e-8)
複数行にわたるマクロ†
マクロは基本的に1行に書かなければならないが,複数行にどうしてもなってしまう場合は,
#define RXFOR2(i0, i1, j0, j1) for(int i = i0; i < i1; ++i) \
for(int j = j0; j < j1; ++j)
#define RXFOR3(i0, i1, j0, j1, k0, k1) for(int i = i0; i < i1; ++i) \
for(int j = j0; j < j1; ++j) \
for(int k = k0; k < k1; ++k)
のように"\"を行末に付ける."\"の後には何も書かないこと(コメントも×).
#undef†
#defineで定義した記号定数,マクロ関数などを無効にする.
#undef PI
#if 〜 #else 〜 #endif†
if文と同じようなもの.
#if 式1
処理1
#elif 式2
処理2
#else
処理3
#endif
式には0や1などの数値を直接指定したり,
defined()でマクロ定義されているかどうかの判別などを指定できる.
#if defined(DEF)
#endif
否定"!"も使える
#if !defined(DEF)
#endif
式にはC言語で使えるものはほとんど使える(==や<,<=,&&,||など)
また,#ifdefを使えば,#if defined()と同じとなる
#ifdef DEF
#endif
否定の場合は
#ifndef DEF
#endif
既定義マクロ†
コンパイラによって事前に定義されているマクロがいくつかある.
- __FILE__ : コンパイルしているファイル名
- __LINE__ : コンパイルしている行の行番号
- __DATE__ : 現在の日付
- __TIME__ : 現在の時間
- __STDC__ : コンパイラが規格に準拠しているかどうか.準拠していれば1,そうでなければマクロ自体が定義されていない.
Visual Studio 2005の場合,プロジェクトのプロパティから「C/C++」->「言語」の「言語拡張を無効にする」を
「はい」にすれば定義される.
マクロ置き換え演算子"#","##"†
- # : マクロ実引数を文字列化する.
#define str(x) #x
char *xyz = "abc";
printf("%s\n", xyz);
printf("%s\n", str(xyz));
表示は
abc
xyz
となる.
- ## : 前後の句を結合
#define add(x, y) data##x + data##y
int data1, data2;
add(1, 2); // data1 + data2
プリプロセッサによるインクルードガード†
ヘッダファイルなどが複数回読み込まれるのを防ぐのに#ifndefを用いる.
#ifndef _HEADER_H_
#define _HEADER_H_
ヘッダの内容
#endif // _HEADER_H_
"_HEADER_H_"はヘッダファイルごとにユニークなものを用いる.
C言語からC++のヘッダを読み込む†
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
入出力ストリームの書式指定†
C++の入出力ストリームは変数の方を気にせずに使えるので便利だが,
小数点以下の表示制度などを操作したい場合などもある.
そのときに使えるのがマニピュレータ.
#include <iomanip>
マニピュレータの一覧は以下.
(using namespace std; にしていることを前提として記述)
マニピュレータ | 説明 | 使用例 |
endl | 改行してバッファをフラッシュ | cout << "Hello world!" << endl; |
flush | バッファのフラッシュのみ | cout << "Hello world!" << flush; |
ends | 文字列の終端文字'\0'を出力 | cout << ends; |
ws | 空白文字を飛ばす入力 | cin >> ws >> str; |
oct,dec,hex | それぞれ8,10,16進数で入出力 | cout << hex << x << endl; |
setw | 値の入出力幅指定 | cout << setw(5) << x << endl; /* xを5桁で表示 */ |
left,right | 値を左寄せ,右寄せで出力 | cout << setw(5) << left << x << endl; |
setfill | 出力幅に満たない部分を埋める文字を指定 | cout << setw(8) << setfill('0') << i << endl; |
また,cout,cinにはメンバ関数setf(), unsetf()があり,これらによりまとめて設定できる.
引数 | 説明 |
ios::skipws,ios::noskipws | 先頭の空白文字を飛ばすかどうか |
ios::unitbuf,ios::nounitbuf | 出力処理のたびにバッファをフラッシュするかどうか |
ios::dec,ios::hex,ios::oct | それぞれ8,10,16進数で入出力 |
ios::showbase | 0xなどの基数表示 |
ios::fixed | 10進表記法(浮動小数点数) |
ios::scientific | 指数表記法(浮動小数点数),1.0e-3など |
ios::showpoint | 小数点を常に表示 |
ios::showpos | 正の符号も付ける |
ios::uppercase | 16進表記のアルファベットを大文字に |
ios::left,ios::right | 左寄せ,右寄せ |
ios::internal | 符号を左寄せ,値を右寄せで表示 |
ios::boolalpha | bool型の出力に"true","false"を使用 |
引数は"|"で結合できる.
そのほか,coutのメンバ関数を用いた書式指定としては,
スリープ†
- 標準ライブラリで
http://www.bohyoh.com/CandCPP/FAQ/FAQ00071.htmlを参考.
1
2
3
4
5
6
7
8
9
10
11
12
13
| | #include <time.h>
int sleep(unsigned long x)
{
clock_t s = clock();
clock_t c;
do{
if((c = clock()) == (clock_t)-1){
return 0;
}
}while(1000UL*(c-s)/CLOCKS_PER_SEC <= x);
return 1;
}
|
ただし,マルチタスク系のOSではCPU使用率が100%になって他のアプリケーションが動けなくなるのであまり使わない方がよい.
時間計測†
- 標準ライブラリ(ANSI C)
clockを用いる.精度はCPU時間であるが,実際には1/100秒ぐらい?
とインクルードして,
1
2
3
4
5
6
7
| | clock_t t1, t2;
t1 = clock();
t2 = clock();
printf("%f [sec]\n", (double)(t2-t1)/CLOCKS_PER_SEC);
|
- Linux
gettimeofdayを用いる.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| | #include <sys/time.h>
double gettimeofday_sec(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec*1e-6;
}
int main(void)
{
double t1, t2;
t1 = gettimeofday_sec(void);
t2 = gettimeofday_sec(void);
printf("%f [sec]\n", t2-t1);
return 0;
}
|
もしくは,getrusageを用いる.
1
2
3
4
5
6
7
8
9
10
| | #include <sys/time.h>
#include <sys/resource.h>
double getrusage_sec(void)
{
struct rusage r;
struct timeval tv;
getrusage(RUSAGE_SELF, &r);
tv = r.ru_utime;
return tv.tv_sec + tv.tv_usec*1e-6;
}
|
- Windows
Windowsでの時間計測には,GetTickCountやtimeGetTimeなどがある.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| | #include <windows.h>
int main(void)
{
DWORD t1, t2;
t1 = GetTickCount();
t2 = GetTickCount();
printf("%d [msec]\n", t2-t1);
return 0;
}
|
GetTickCount()はWindowsが起動してからの時間をミリ秒単位で返す.
精度的には10-20msぐらいのようである.
さらに精度が必要なときは,timeGetTimeを用いる.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| | #pragma comment (lib, "winmm.lib")
#include <mmsystem.h>
int main(void)
{
DWORD t1, t2;
t1 = timeGetTime();
t2 = timeGetTime();
printf("%d [msec]\n", t2-t1);
return 0;
}
|
winmm.libをリンクしている.精度は1-2msぐらい.
マイクロ秒単位での精度が必要な場合はさらにQueryPerformanceCounterがある.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| | #include <windows.h>
int main(void)
{
LARGE_INTEGER t1, t2;
LARGE_INTEGER f;
QueryPerformanceFrequency((LARGE_INTEGER*)&f); QueryPerformanceCounter((LARGE_INTEGER*)&t1);
QueryPerformanceCounter((LARGE_INTEGER*)&t2);
printf("%f [msec]\n", (double)(t2.QuadPart-t1.QuadPart)/(double)(f.QuadPart));
return 0;
}
|
システムに高分解能パフォーマンスカウンタがあれば,それに応じた高分解能が得られる.
なければ,各関数は変数に0を格納して返す.
Windows上では最も高分解能が得られる方法の一つではあるが,カウンタに何が使われるのかが環境によって変わるので,
QueryPerformanceFrequencyの値を一度見ておいた方がよいかもしれない
(CPUクロックが使われていたらならば,分解能は高いがクロック可変なCPUの場合に問題があるかもしれない).
std::string で CString::Format や printf のように文字列を入力†
std::ostringstream を使用.
#include <sstream>
#include <string>
std::ostringstream stream;
stream << "step : " << i;
std::string str = stream.str();
RTTI†
RTTI(run-time type identification)は日本語では,実行時型情報となり,そのまんま実行時に型の情報を得る機能である.
最新のANSI C++ではサポートされているので,
#include <typeinfo.h>
として,
int x = 10;
cout << "type : " << typeid(x).name() << endl;
や
int x = 10;
if(typeid(int) == typeid(x)){
xがint型だったときの処理
}
という風に使える.
このRTTIは,クラスのポリモーフィズム(多態性)を使うときに便利である.
GLUTなどでDOS窓を出さない†
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
複数モニタの検出 - Platform SDK GDI†
#include <windows.h>
EnumDisplayMonitors(...);
GetMonitorInfo(...);
など
MSDN参照.