• 検索結果がありません。

C 資料 電脳梁山泊烏賊塾 キャスト C++ のキャスト 初めに 此処では Visual Studio 2017 を起動し 新しいプロジェクトで Visual C++ の Windows デスクトップを選択し Windows コンソールアプリケーションを作成する C++ でのキャスト

N/A
N/A
Protected

Academic year: 2021

シェア "C 資料 電脳梁山泊烏賊塾 キャスト C++ のキャスト 初めに 此処では Visual Studio 2017 を起動し 新しいプロジェクトで Visual C++ の Windows デスクトップを選択し Windows コンソールアプリケーションを作成する C++ でのキャスト"

Copied!
6
0
0

読み込み中.... (全文を見る)

全文

(1)

■ C++のキャスト ■ ■ 初めに

此処では、Visual Studio 2017 を起動し、新しいプロジェクトで、Visual C++の Windows デスクトッ プを選択し、Windows コンソールアプリケーションを作成する。 ■ C++でのキャスト C++では其のキャストを 3 種類に分類して居る。 ■ 静的な普通の型変換(static_cast) 先ずは1つ目は、静的な普通の型変換で有る。下記のコードを観て欲しい。 #include <stdio.h> int main() { signed char a = -1; int b;

キャ

ャス

スト

(2)

b = a; printf("%08X (%d)¥n", b, b); b = (unsigned char)a; printf("%08X (%d)¥n", b, b); return 0; }

signed char から unsigned char への型変換にキャストを使用して居る。キャストとして一番普通の使 用法で有る。

亦、void*から char*へのキャスト、クラスへのポインタのアップキャスト、ダウンキャスト(アップキ ャストの逆)等も此の範疇に入る。

斯う謂ったキャストには、C++では static_cast 演算子を使用する。 a = static_cast<char>(b); // char a; int b;

pszPath = static_cast<char*>(buffer); // char* pszPath; void* buffer; pFile = static_cast<CFile*>(pTxtFile); // CFile* pFile; CTextFile* pTxtFile; pTxtFile = static_cast<CTextFile*>(pFile); 書式は、下記の様に成る。 static_cast< 型 >( 変換し度いデータ ) 此の型変換の時、実行時に自動的に安全性を確認する事は無い。例えば int の値を char に代入する時に 桁溢れするか何うかとか、ダウンキャストの時に其れは安全なのかとかで有る。 只、実行しなくても危険な変換だと解る時はコンパイルエラーに成る。例えば、ポインタから int への 変換、int からポインタへの変換、int へのポインタから double へのポインタへの変換、全く関係無い クラスへのポインタ同士の型変換等が然うで有る。

基本的には無茶な型変換では無いが、使い方を間違えれば危険な事も有る(特にダウンキャスト)。上 記の例では、pFile に pTxtFile を代入した後に pFile を CTextFile に代入しようとして居る。此れは問 題無いが、若し、pFile に CBinaryFile クラスへのポインタの値が入って居たとすると、CBinaryFile と CTextFile 同士には直接の継承関係は無いので危険で有る。CBinaryFile から CTextFile へ直接 static_cast する事は出来ないが、CFile を介する事で出来て了う。 多少然う謂う危険性は有る物の、基本的に static_cast は、ポインタに関して厳しい丈の普通のキャス トと謂う事に成る。普通は此のキャストを使う事に成ると思う。 ■ ポインタの関係する強引な型変換(reinterpret_cast) 併し、意図的に危険なキャストを仕度い時も有る。例えば、Windows プログラムを遣って居ると、 unsigned int とポインタの相互変換をしなければ成らない時が有る。然う謂う時のキャストが 2 番目の キャスト、ポインタの関係する強引な型変換で有る。 #include <iostream.h> // MultiFunc に渡されたデータの種類 enum EMFType

(3)

{ MF_INT, // 符号付き整数 MF_UINT, // 符号無し整数 MF_FLOAT, // 単精度小数へのポインタ MF_DOUBLE, // 倍精度小数へのポインタ MF_STRING, // 文字列 }; // 色々なデータを1つの関数で表示

void MultiFunc(EMFType type, unsigned int data) {

switch(type) {

case MF_INT : cout << (int)data << endl; break; case MF_UINT : cout << data << endl; break; case MF_FLOAT : cout << *(const float*)data << endl; break; case MF_DOUBLE: cout << *(const double*)data << endl; break; case MF_STRING: cout << (const char*)data << endl; break; } } int main() { int a = -10; unsigned int b = -10; float c = -10.3f; double d = 3.14159265358979; const char* e = "えいっ"; MultiFunc(MF_INT , a); MultiFunc(MF_UINT , b);

MultiFunc(MF_FLOAT , (unsigned int)&c); MultiFunc(MF_DOUBLE, (unsigned int)&d); MultiFunc(MF_STRING, (unsigned int)e);

return 0; }

MultiFunc は第 1 引数の値に依りデータを何う解釈するかを決める。其れは整数値で有ったり、ポイン タで有ったりする。unsigned int とポインタのサイズは同じなので、unsigned int にポインタを代入す るのも、其の逆も、問題は無い。

上記のキャストの内、unsigned int から int へのキャストは普通のキャストだが、float*から unsigned int へのキャストや unsigned int から const char*へのキャストは例の危険なキャストで有る。併し、此 処では使い方に注意して居るので、普通は可笑しな事には成らないだろう。斯う謂う事を仕度い時、又 は、せざるを得ない時、static_cast しか許されて居なければ困る事に成る。

斯う謂うキャストに使うのが reinterpret_cast 演算子で有る。リインタープリットキャストと読む。 MultiFunc(MF_FLOAT, reinterpret_cast<unsigned int>(&c));

cout << reinterpret_cast<const char*>(data) << endl; 書式は static_cast と同じで有る。

(4)

reinterpret_cast はポインタの関係する危険なキャストを強引に行うので、通常は使わない。特殊な状 況で而巳使う様にす可きで有る。 普段のキャストを static_cast を使う様に仕て居れば、間違って変な変換を記述してもエラーに成る。 意図的に使う時丈 reinterpret_cast を使うようにすれば、比較的安全にキャストを行う事が出来るだろ う。 猶、reinterpret_cast の使い方には、危険な参照へのキャストも含まれる。 float f = 8.9f; // hex を挟むと、16 進数で出力する

cout << hex << reinterpret_cast<int&>(f) << endl; cout << hex << static_cast<int>(f) << endl;

float と int のサイズは(32 ビット機では)同じなので、float の内容を其の儘 16 進数で書き出す事が 出来る。此れを static_cast<int>で行うと小数点以下が切り捨てられた 8 と表示される。代入に関して も reinterpret_cast<int&>(f) = a;(a の型は int)と謂う風に出来る。

併し、此等は基本的に危険だと謂う事は忘れないで欲しい。reinterpret_cast を使う時は充分に気を付 けて欲しい。 ■ const 外し(const_cast) 最後のキャストは、const 外しで有る。其の名の如く、ポインタや参照に付いた const を外す為のキャ ストで有る。 #include <iostream.h> void Func(char* str) { cout << str << endl; } int main() {

const char szMsg[] = "const がついとらんじゃと!"; Func((char*)szMsg);

return 0; }

Func 内では str は変更されないが、str の型には const が付いて居ない。此の儘では const なデータで 有る szMsg のアドレスを渡す事は出来ない。 斯う謂う事は殆ど無いとは思うが、万が一有った時の為にキャストで const を外す事が出来る。 併し、此れは非常に危険な事なので、然う軽々出来て欲しくは無い。其処で、const は static_cast で、 亦、reinterpret_cast ですら外す事は出来ない。 斯う謂う場合は、専用の演算子 const_cast を使用する。 Func(const_cast<char*>(szMsg));

(5)

併し、Func が自作の関数なら、Func の定義を変える可きで有る。変えられない状況の場合に、止む無 く const_cast を使う事に成る。

const の他にも volatile と謂う物も外す事が出来る。volatile は其の変数に対して最適化を行わない様に する為の型修飾子で有る。

他にも、const メンバ関数から非 const メンバ関数を強引に呼ぶ必要の有る場合にも、this を const_cast する事に依って実現出来る。只、危険なので遣らないに越した事は無い。

実用的には、非 const メンバ関数の中で const メンバ関数の戻り値の const を外すと謂う様な限定的な 状況で使う事に成るだろう。

■ ダウンキャスト

先ずは、前述の CFile と CBinaryFile と CTextFile の話を思い出して欲しい。CBinaryFile クラスのオ ブジェクトのアドレスを一旦 CFile*に渡した物を CTextFile*にキャストすると困ると謂う物で有る(此 処で、CBinaryFile と CTextFile は CFile の派生クラスで有る)。

CBinaryFile ではバイナリ形式でファイルを開いて居るので、其れを使用してテキスト形式で開いて居 る事を前提として処理をしようとしても可笑しく成る。亦、此処では問題に成らないが、派生クラス特 有のメンバ変数が有ると、他方には其のメンバ変数が無いので処理が可笑しく成る。 基底クラスから派生クラスへのキャストはダウンキャストと呼ぶが、此の様にダウンキャストは危険を 伴う。 従って、オブジェクトの真の型と其の継承関係を厳密にチェックし、不正なキャストをするとエラーを 返す様なキャストが C++では必要に成って来る。其れが dynamic_cast で有る。文法は他の 3 種のキャ ストと同じで有る。 CBinaryFile bin;

CFile* pFile = dynamic_cast<CFile*>(&bin); CTextFile* pText = dynamic_cast<CTextFile*>(pFile); CBinaryFile* pBin = dynamic_cast<CBinaryFile*>(pFile);

上記の例では、CBinaryFile*から CFile*へのキャストは特に問題無いが、3 行目では更に其れを CTextFile*でキャストしようとして居る。此れが前述の困るキャストで有る。此の様に困るキャストを すると、dynamic_cast は NULL を返す。戻り値が NULL の時はエラー処理をすれば良い訳で有る。 4 行目では、元が CBinaryFile なので正しいキャストで有る。此の場合は問題無くキャストが行われる。 此の様に、dynamic_cast を使えば真のオブジェクトの型と其の継承関係をチェックして下れる訳で有 る。若し、此処で static_cast を使うと、両方共其の儘キャストされて了う。 dynamic_cast が使えるのは仮想関数を持ったクラスに限る。此の様なクラスをポリモーフィック(多 様性を持った)クラスと呼ぶ。併し、デストラクタを必ず仮想関数にするので、実質問題此の制限は無 いのと同じ事で有る。 dynamic_cast では、void*へキャストする事も出来る。併し、void*からのキャストは行えない。 多重継承と謂う物をすると、今少し色々な事が起こる。クロスキャストと謂うのに出くわす事も有るか も知れない。亦、アップキャストでも失敗する事も出て来るだろう。併し、多重継承に付いては行う可 きではないので、此処では取り上げない。

(6)

dynamic_cast を使用するとエラーが出て来るかも知れない。少なくともVC++では然うで有る。 実は、dynamic_cast を使うにはランタイムタイプ情報(RTTI:Run-Time Type Information)と謂う 物が必要なので有る。此れがないとコンパイルエラーに成る。 ランタイムタイプ情報を有効にするには、VC++ではプロジェクトの設定を変更する必要が有る。プロ ジェクトの設定は、Alt+F7 で開く事が出来る。 此処の「C++」タブの「C++言語」カテゴリの処に「ランタイムタイプ情報 (RTTI) を有効にする」と 謂うのが有ると思う。此れをチェックして OK ボタンか Enter キーを押せば設定終了で有る。 此処で注意する事は、ダイアログの左上に有る「設定の対象」の横に有る指定を「全ての構成」に仕て 置 く と 謂 う 事 で 有 る 。 ビ ル ド の 構 成 が 変 わ っ た 時 に ラ ン タ イ ム タ イ プ 情 報 が 有 効 に 成 ら ず 、 dynamic_cast の処でエラーが出て了うからで有る。 設定を忘れてもコンパイルエラーが出た時に設定を仕直せば良い丈なのだが、慣れない内は原因が特定 し難いかも知れない。プログラムを作ったばかりの時は良いのだが、完成したと思った時に斯う成ると 混乱して了うかも知れない。然う謂う事の無い様、設定は正しく仕て置いた方が良い。 最後に。dynamic_cast を使うと処理が遅く成るのではないかと心配に成るかも知れないが、チェック する必要の無いキャスト(普通のアップキャスト)では static_cast と同じ処理に最適化される。ダウ ンキャストは其れ程使う事はないので、速度を心配する事は無いと謂う事で有る。 従って、アップキャスト、ダウンキャストには積極的に dynamic_cast を使うと良いだろう。 要点を下記に記す。 ・dynamic_cast を使えば継承関係をチェックして下れる。 ・不当なキャストの時は NULL を返す。 ・dynamic_cast を利用するには、ランタイムタイプ情報が必要で有る。

参照

関連したドキュメント

実際, クラス C の多様体については, ここでは 詳細には述べないが, 代数 reduction をはじめ類似のいくつかの方法を 組み合わせてその構造を組織的に研究することができる

昭33.6.14 )。.

Bluetooth® Low Energy プロトコルスタック GUI ツールは、Microsoft Visual Studio 2012 でビルドされた C++アプリケーションです。GUI

Windows Hell は、指紋または顔認証を使って Windows 10 デバイスにアクセスできる、よ

Visual Studio 2008、または Visual Studio 2010 で開発した要素モデルを Visual Studio

(( .  entrenchment のであって、それ自体は質的な手段( )ではない。 カナダ憲法では憲法上の人権を といい、

▼ 企業名や商品名では無く、含有成分の危険性・有害性を MSDS 、文献

   遠くに住んでいる、家に入られることに抵抗感があるなどの 療養中の子どもへの直接支援の難しさを、 IT という手段を使えば