読書ノートEffective_C++_条項二十七:転換動作をできるだけ少なくする

6791 ワード

モデルチェンジに関するいくつかの方法は、以前のブログに書かれています.ここではまず簡単に振り返って、effectiveでもっと深く述べます.
転換は風格によってC風格の転換とC++風格の転換の2種類に分けることができて、C風格の転換はとても見やすくて、私達がよく使うため、
(T) expression

および:
T (expression)

最も古典的な例は整数除算を処理することであり、C/C++プログラムでは、整数除算の結果は整数であり、3/5のような結果が得られない場合があります.結果は0.6ではなく0ですが、double(3)/5であれば、結果は0.6になります.double(3)を操作し、整数を浮動小数点数に変換するため、小数除法になります.小数点以下の結果が得られます.
 
C++スタイルの転換操作は4種類に分けられる.
const_cast<T>(expression)
static_cast<T>(expression)
dynamic_cast<T>(expression)
reinterpret_cast<T>(expression)

effectiveは上位3種を推奨し、最後の1つは1つのポインタを整数に変換したり、整数をポインタと見なしたりすることで、プラットフォームへの依存性が強く、使用を推奨しません.
 
const_castは簡単です.定数属性を削除します.たとえば、次のようにします.
1 int main()
2 {
3     int a= 3;
4     const int* ca = &a;
5     int *pb = const_cast<int*> (ca);
6     *pb = 5;
7     cout << a << endl; // a      5
8 }

しかし、このような定数の変換は、ポインタまたは参照(thisポインタを含む)に対してのみ行われ、通常の変数に対しては行われません.例えば、次のような方法は間違っています.
1 int main()
2 {
3     const int a = 3;
4     int b = const_cast<int>(a);  //    ,    static_cast  C    
5 }

const_の場合castは、const->非constであってもよいし、非const->constであってもよい.2点を覚えていれば、この操作はポインタまたは参照にのみ有効である.2つ目は、変換されたオブジェクトの通常のプロパティを変更することではありません.
1 int a= 3;
2 const int* ca = &a;
3 int *pb = const_cast<int*> (ca);

Caは依然として定数のポインタを指しており、*ca=5のような操作は、誤訳を報告します.
 
static_castは最もよく使われるC++の転換であり、私たちがよく見ているint->doubleやfloat->intなどはstatic_を使っています.castは、通常ポインタ/参照->non通常ポインタ/参照でない限り、const int->int、およびint->const intを含む変換を行います.
 
dynamic_castはベースクラスポインタを派生クラスポインタに変換したい場合に使用され、この変換は安全性を保証し、2つの関係のないクラス間をdynamic_castの場合、変換の結果は空のポインタになります.また、ベースクラスポインタが指すオブジェクトがベースクラス自体である場合、ベースクラスポインタをdynamic_castは派生クラスに下向きに移行し,空のポインタが得られ,さらなる操作を防止する.
 
ここまで紹介するには、古いモデルチェンジが十分である以上(結局、C言語はこのように使われているので、モデルチェンジの機能が豊富ではないと文句を言う人はいません)、なぜC++は機能の繰り返しのモデルチェンジを定義しなければならないのでしょうか.
原因は2つあります.第一に、細分化された転換はコードの中で認識されやすく、コンテキストを見ずにこのような転換がどんなタイプなのか(定量的転換か下向き転換か)を知ることができます.第二に、各転換動作の目標が狭くなるほど、コンパイラは誤った応用を診断する可能性がある.
 
次に、effectiveでは、できるだけ転換動作をしないようにしなければならない理由を説明します.
原理の転換が深刻なバグをもたらすことを知らない.例えば、本で述べたように、ベースクラスWindowがあると仮定すると、派生クラスSpecialWindowがあり、OnResize()のメンバー関数があり、派生クラスのOnResize()でベースクラスのOnResize()を先に呼び出そうとすると、プログラマーがこうした.
1 void SpecialWindow::OnResize()
2 {
3     static_cast<Window>(*this).OnResize();
4 5 }

これで本当に大丈夫ですか?派生クラスオブジェクトをWindowオブジェクトに変換し、OnResize()を呼び出します.これは確かにベースクラスのOnResize()を呼び出しますが、OnReszie()のメンバー変数操作の結果はあなたが望んでいますか?
答えは否定的だstatic_castは、派生クラスを本当に構成するそのベースクラスではなく、そのコピー(このコピーのメンバー変数値は派生クラスオブジェクトのベースクラスの成分と同じであるが、アドレスが異なる)を生成し、OnResize()を呼び出して変更した結果、このコピーのメンバー変数が変化し、しかし、派生クラスに含まれるベースクラスのメンバー変数には何の変化もありません.はい、これがバグです.その後のテストでこの問題はプログラマーをしばらく悩ませます.
一言で言えば、転換して生成されたのはcopyで、コピーで、時にはこれはあなたが望んでいるものではありません.修正方法は実は簡単です.
1 void SpecialWindow::OnResize()
2 {
3     Window::OnResize(); // OK ,     
4 5 }

dynamic_のような転換操作も廃棄されていますcast,この転換はクラス名をstrcmpして,下への転換が合理的かどうかを判断し,継承深さが比較的大きい場合,毎回dynamic_castは複数回strcmpを行い、プログラムの実行効率に深刻な影響を及ぼす.解決策は,可能であれば,直接サブクラスを指すポインタを用い,本当に親のポインタ(例えば工場設計モードなど)を使いたい場合は,多態を考慮し,親の対応する関数の前にvirtualを加え,サブクラスを上書きすればよい.
 
最後にまとめると、あるプログラマーは、転換は実は何もしていないと思っています.ただ、あるタイプを別のタイプと見なすようにコンパイラに伝えただけです.ポインタと言えば、このように理解するのは正しいですが、すべてを言うと(変数も含めて)、static_のように妥当ではないかもしれません.cast(a)/bという古典的な整数除算->小数除算変換では,変換前後に下位層で生成されるコードは絶対に異なる.
1.できれば、転換をできるだけ避け、特に効率を重視するコードでdynamic_を避けるcasts.
2.転換が必要な場合は、ある関数の背後に隠してみてください.お客様は、転換を自分のコードに入れる必要がなく、その後、この関数を呼び出すことができます.
3.C++スタイルの新しいスタイルを使うほうがいい.Cスタイルを使わないほうがいい.前者は見分けやすいし、分類された職掌もあるからだ.