疑問解決(アマチュアプログラミング) C++編

2012/5/5

ここでは、素人のプログラマがひかかりそうなプログラミング技術を紹介します。


・C++
 

クラスポインタ同士の”=”演算
ポインタ変数(MyClass* pMyClass 等)とは、変数またはクラスのアドレスを保持しています。
そのため、アドレスを代入したい場合は下記となります。
この場合、pbを操作すれば cMyClass1の値が変更されます。
(pbは、 cMyClass1のアドレスをさしているため)
 MyClass cMyClass1; // クラス宣言
 MyClass* pa; // ポインタ変数宣言
 MyClass* pb; // ポインタ変数宣言

 pa = &cMyClass1; // pa には、 cMyClass1のアドレスが入ります。
 pb = pa; // pb にも、 cMyClass1のアドレスが入ります。

では下記はどうでしょうか?
この場合、pbを操作してもcMyClass1の値は変更され無い場合があります。
(pbは、 cMyClass2のアドレスをさしているため、
 メンバ変数にポインタを持っていない場合は変更されません。
 ポインタ変数をメンバにしている場合は、そのポインタ(アドレス)がコピーされてくるので、
 cMyClass1の値(メンバポインタがさしているインスタンス)が変更されます。 )

 MyClass cMyClass1; // クラス宣言
 MyClass cMyClass2; // クラス宣言
 MyClass* pa; // ポインタ変数宣言
 MyClass* pb; // ポインタ変数宣言

 pa = &cMyClass1; // pa には、 cMyClass1のアドレスが入ります。
 pb = &cMyClass2; // pb には、 cMyClass2のアドレスが入ります。
 *pb = *pa; // cMyClass2 は、 cMyClass1の値が代入されます。

メモリマップドファイル
Windowsにおいて、メモリ空間とファイル空間のデータの持ち方は似ています。
しかし、メモリはアドレスがありポインタを使用できますが、ファイルはアドレスがありませんしポインタを使用できません。
そこでファイルをメモリ方式でアクセスできるようにする方法が「メモリマップドファイル」です。
勘違いしやすいのは、あくまでアクセス方法が変わるだけで、
実際にファイル内容をメモリに持ってきているわけではありません。

const
constは、定数を表す修飾詞です。
constを使う事によって、コードの意味を明確にする事ができますし、
コンパイル速度も速くなる場合があります。
const 変数
 変数の宣言に const をつけると、その変数の値は変更できなくなります。

const 引数
 引数に const をつけると、その関数の中では値を変更できません。
 const 引数はよく参照引数と使います。

const ポインタ
 ポインタ変数の宣言に const をつけると、ポインタの中身が変更できません。
const メンバ関数
 メンバ関数の宣言の末尾に const 修飾子をつけると、
 そのメンバ関数を呼び出したときにオブジェクトが変化しないことを宣言します。
 const オブジェクトのメソッドは、constメンバメソッドしか呼び出すことは出来ません。

開発環境作成ファイル
VCやBuilderなど、開発環境によって
オリジナルの設定ファイル等を作成します。
削除しても再度作成されますので問題ありません。
VisualC++Ver6
 *.plg *.ncb *.opt

Borland C++Builder
 *.tds


C言語には様々な型があります。
基本的な型としては、int・short・doubleなどですね。
しかし、Windows特有のLPSTRなる物も存在します。
(単に基本の型をtypedefしてるだけですが)
この辺を解説したいと思います。
まず基本形
 1バイト文字列は char
 UNICODE等2バイト文字列は wchar_t
  typedef short wchar_t

次に”WCHAR”ですが、これは単なる”wchar_t”です。
 typedef wchar_t WCHAR
TCHARは、環境依存の1バイトまたは2バイト文字です。

#ifdef UNICODE
 typedef WCHAR TCHAR;
#else
 typedef char TCHAR;
#endif 

最後にLPSTR系
 LPSTR(Long Pointer String)は、charへのポインタです。
  typedef char* LPSTR
 これに”const”がついたものがLPCSTR
  typedef const char* LPSTR
 上記のWCHAR対応が、LPWSTRとLPCWSTR
 TCHAR対応が、LPTSTRとLPCTSTR
となります。

関数ポインタ
関数へのポインタは、使う機会は少ないですが非常に有用です。
引数と戻り値を宣言時に指定する必要があります。

関数ポインタの変数名をpFunc とした場合
int (*pFunc)(int, int);
となります。
括弧を忘れて
int* pFunc(int, int);
としてしまうと、単なるintのポインタを返す関数の宣言となりますので注意してください。

関数ポインタの配列を宣言したい場合は、
int (*pFunc[8])(int, int);
となり、[]の位置が普通の変数の場合と違い、
内側になりますので注意してください。

またメンバ関数へのポインタは、
完全に限定されたメンバ名で指定する必要があり、
継承されたクラスであっても、代入する事はできません。

ただしC++Builderでは、
"__closure"という拡張キーワードがあり、
これを使う事によって違うクラスであっても代入できるようになります。

int (__closure *apFunc[3])(void)


プロセス間通信(Interprocess Communications, IPC)
プロセス間通信を行う場合、下記の方法があります。
方法 解説
メールスロット メッセージをポストする方法
パイプ シェルのパイプ処理を拡張したもの。
双方向でも通信可能
名前付きパイプと名前無しパイプがある。
マシン内でのパイプ処理は高速ですが、
マシン間で使う場合は、
通信確認用のデータが飛び交うため、
ソケット通信を使ったほうが高速です。
アトム  
クリップボード  
DDE  
フック  
ファイルマッピング  
名前無しパイプ

名前無しパイプは親子プロセス間で使用します。

使用例

1.名前無しパイプを作成

BOOL CreatePipe(
      PHANDLE hReadPipe,              // Readパイプハンドルのポインタ
      PHANDLE hWritePipe,             // ハンドルのポインタ
      LPSECURITY_ATTRIBUTES lpSecAtt,    // SECURITY_ATTRIBUTES構造体へのポインタ。
                                 // ハンドルを継承するかどうかを決める。
      DWORD nSize                   // パイプのバッファサイズを指定。0にするとデフォルト
);
SECURITY_ATTRIBUTES
オブジェクトに関するセキュリティデスクリプタが格納されます。
この構造体を指定することで、オブジェクトのハンドルが継承可能であるかどうかを指定します。

 

typedef struct _SECURITY_ATTRIBUTES {
 DWORD nLength; // 構造体サイズ
 LPVOID lpSecurityDescriptor; // セキュリティデスクリプタ
 BOOL bInheritHandle; // 継承フラグ
} SECURITY_ATTRIBUTES;

メンバ
nLength
 SECURITY_ATTRIBUTES 構造体のサイズをバイト単位で指定します。
 12 を指定します。

lpSecurityDescriptor
 初期化されたセキュリティデスクリプタのアドレスを指定します。
  0 (NULL) を指定すると、
  呼び出しプロセスのデフォルトのセキュリティデスクリプタが指定されます。

bInheritHandle
 オブジェクトのハンドルが継承されるようにするかどうかを指定します。
 TRUE  継承される。
 FALSE  継承されない。

2.小プロセスのロード
CreateProcess
 
typedef struct _PROCESS_INFORMATION {
  HANDLE hProcess; // プロセスのハンドル
  HANDLE hThread; // プライマリスレッドのハンドル
  DWORD dwProcessId; // プロセスID
  DWORD dwThreadId; // プライマリスレッドID
} PROCESS_INFORMATION;
メンバ
hProcess
 新しく作成されたプロセスのハンドルが格納されます。

hThread
 新しく作成されたプライマリスレッドのハンドルが格納されます。

dwProcessId
 プロセスを識別するのに使われるグローバルプロセス ID が格納されます。

dwThreadId
 プライマリスレッドを識別するのに使われるグローバルスレッド ID が格納されます。

 
STARTUPINFO



 
 3.書込ハンドルを閉じる
 4.ReadFile関数で読込
 5.読込ハンドルを閉じる

名前付きパイプ
名前付きパイプはネットワーク経由で使用する事が出来ます。

 
CRT(C RunTime)関数
C++既存の関数では文字列操作等の際、
バッファーオーバーランを起こす危険性があるため安全ではありません。
そこで、VisualStudio2005より安全性を確保した新しい関数群を作成しました。
そのため、VS2005以降を利用した際、既存の関数を利用した場合は、
Warningが出るようになっています。
Warningを出さないようにするためには、
新しい関数群を利用するか、ソースの先頭に以下のDefineを書いてください。
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
 (旧関数を新関数に置き換えます。)
または
#define _CRT_SECURE_NO_DEPRECATE
 (警告を出さないようにします。)

ちなみに置き換わった関数の一例は下記の通りです。
旧関数 新関数
sprintf sprintf_s

・CGI
 CでCGIを作ることも可能です。
 その場合、WEBブラウザから送られてきたGETやPOSTデータは、
 getenv関数を使って環境変数から取得します。

 サンプルを示します。
char* p = getenv("REQUEST_METHOD");
失敗した場合はNULLが返ります。
 
用語集 解説
CONTENT_TYPE
MIMEタイプ
CONTENT_LENGTH
メソッドがPOSTの際のフォームデータサイズ
HTTP_REFERER
リンク元URL
直接入力、ブックマークからの場合は何も入らない。
GATEWAY_INTERFACE
CGIのバージョン
HTTP_USER_AGENT
ユーザエージェント名
QUERY_STRING
メソッドがGETの際のデータ部分
REMOTE_ADDR
クライアントのIPアドレス
REMOTE_HOST
クライアントのホスト名
REQUEST_METHOD
メソッド(GETまたはPOST)
SCRIPT_FILENAME
CGIプログラムのフルパス
SCRIPT_NAME
SCRIPT_FILENAMEのホスト名を除いたもの
SERVER_ADDR
サーバのIPアドレス
SERVER_ADMIN
サーバ管理者のメールアドレス
SERVER_NAME
サーバのホスト名
SERVER_PORT
サーバのポート番号
SERVER_SOFTWARE
サーバ名とその OSのバージョン
TZ
サーバのタイムゾーン
・列挙型

 列挙型についての説明は、他のサイトで山の様に解説があるのでここでは触れません。
 問題となるのは、列挙型を関数の引数として利用した場合です。

 コンパイラの仕様にも寄りますが、
 列挙型引数に対してint等で宣言した変数を引数として呼び出すとバグる事があります。

 今回、問題があったのはC++BuilderVer6 です。
 (他のコンパイラは未確認)

 原因は下記の通りでした。
 列挙型の宣言で中の要素数が少なかったため、BuilderはBYTE型で問題ないと判断しました。
 これを変数を引数として呼び出した場合、ここで型の不一致が起こります。
 これに対応するためには、コンパイラオプションで、列挙型をintとして扱うようにすれば問題ありません。
 そもそも、引数を列挙型にするということは、
 本来その要素を使って呼ばせたいということなので、変数を使うこと自体駄目なのでしょうね。

・ライブラリ
 マイクロソフトのVC++ と ボーランド(今はコードギアかな)の C++Builder では、
 ライブラリファイル(lib拡張子)の形式が違います。
 (それぞれCOFF形式とOMF形式と言います。)

  そのためボーランドのC++BuilderでLibファイルをプロジェクトに追加してコンパイルする際
 以下のリンカーエラーが発生する場合があります。
 [リンカ エラー] '***.LIB' contains invalid OMF record, type 0x21 (possibly COFF)

  これは、COFF形式のライブラリをBuilderでリンクしようとしたためです。

 解決方法としては、下記2案あります。
 1.COFF形式のライブラリファイルをOMF形式のライブラリファイルに変換する。
  coff2omf.exe を使用します。
  ただしCOFF形式にもバージョンがあり、対応出来ないものもあるため2を推奨
 2.dllファイルからOMF形式のライブラリファイルを作成する。
  implib.exe を使用します。具体的な使用方法は下記のとおり。
  implib xxx.lib xxx.dll
・用語
用語集 解説
   
   
・覚書
 ・コピーコンストラクタ
 ・ヒープ
 ・ガベージ・コレクション
 ・スタック
さらに情報が欲しい方は、Google検索で  
Google
・TOPへ戻る

メールはこちらに