【c++】条項2:できるだけconst,enum,inlineで#defineを置き換える
5759 ワード
“ “ , #define
言語の一部と見なされない.それが問題です
#define ASPECT_RATIO 1.653
ASPECT_RATIO :
前にプリプロセッサに移動されました.そこで記号名ASPECT_RATIOは記号表に入っていない可能性があります.この定数を使用してコンパイルエラー情報を取得すると、ASPETCではなく1.653に言及する可能性があるため、困惑する可能性があります.RATIO.ASPETC_RATIOは、あなたが書いたヘッダファイルではなく、1.653とそれがどこから来たのかについて概念がないと定義されています.そこで、それを追跡するために時間を無駄にします.この問題は、記号式デバッガにも発生する可能性があります.理由は同じです.あなたが使用している名前は記号テーブルに入っていない可能性があります.解決策は、上記のマクロ(#define)const double AspectRatio=1.653を1つの定数で置き換えることである.言語定数として、AspectRatioはコンパイラに見られるに違いありません.もちろん記号表に入ります.また、浮動小数点定数の場合、定数の使用は、プリプロセッサが「マクロ名ASPECT_RATIOを盲目的に1.653に置き換える」ことによって、ターゲットコードの1.653が複数存在する可能性があるため、定数AspectRatioを変更すると同情することはありません.defineを定数で置き換えると、2つの特殊な状況があります.(1)定数ポインタを定義します.定数定義式は通常ヘッダファイルに格納(異なるソースコードに含まれるようにする)ため、ポインタ(ポインタが指すものだけでなく)をconstとして宣言する必要がある.たとえば、上記のauthorNameは、const std::string authorName(「Scott Meyers」)と定義されることが多い.2つ目はclass専属定数です.定数の役割ドメインをclass内に制限するには、メンバーにする必要があります.この定数が最大1つのエンティティのみであることを確認するには、1つのエンティティにする必要があります.
static
class GamePlayer{
private:
static const int NumTurns = 5;//
int scores[NumTurns];//
...
}`
NumTurns 。 c++
任意のものは定義式を提供しますが、class固有定数でstaticで整数タイプの場合は、特別な処理が必要です.アドレスを取らない限り、定義式を指定することなく宣言して使用できます.しかし、class固有の定数のアドレスを取得したり、アドレスを取得しなくてもコンパイラが定義式を見たいと主張したりする場合は、const int GamePlayer::NumTurns;//NumTurnsの定義//では、なぜ数値を与えなかったのかを説明します.この式をヘッダファイルではなく実装ファイルに入れてください.class定数は宣言時に初期値を取得しているため、定義時に初期値を再設定することはできません.ちなみに、definesは役割ドメインを重視していないため、class固有定数を作成することはできません.マクロが定義されると、その後のコンパイルで有効になります.これは
#defines
class , , private #define 。 const ,NumTurns
そうです.古いコンパイラは上記の構文をサポートしていないかもしれませんが、staticメンバーが宣言式で初期値を取得することはできません.また、いわゆる「in-class初期値設定」は整数定数のみを許可します.コンパイラが上記の構文をサポートしていない場合は、初期値を定義式に置くことができます.
class CosEstimate
{
private:
static const double FudgeFactor;//static class
...//
const double//static class
CostEstimate::FudgeFactor = 1.35;//
};
これはほとんどあなたがいつでも唯一しなければならないことです.唯一の例外は、classコンパイル中にclass定数値が必要な場合です.たとえば、前述のGamePlayer::scoresの配列宣言式です.このとき、もしあなたのコンパイラが「static整数型class定数」が「in class初値設定」を完了することを許さない場合は、いわゆる「the enum hack」補償方法に変更します.その理論的基礎は、「列挙タイプに属する数値がintsを充てるために使用される」ことであり、GamePlayerは以下のように定義することができる.
class GamePlayer
{
private:
enum{NumTurns = 5;
int scores[NumTurns];
...
}
}
ある理由に基づいてenum hackは私たちが認識する価値があります.第一に、enum hackの行為はある面で似ています.
#define const, 。
たとえばconstのアドレスを取るのは合法ですが、enumのアドレスを取るのは合法ではありません.defineのアドレスを取るのは一般的に合法ではありません.pointerやreferenceが整数定数を指している場合、enumはこの制約を実現するのに役立ちます.また、優れたコンパイラは「整数型constオブジェクト」に別のストレージスペースを設定しません(pointerやreferenceがオブジェクトを指していない限り)、優れていないコンパイラはそうかもしれませんが、これは望ましくないかもしれません.Enumsは#definesと同様に、不要なメモリ割り当て認識enum hackを招くことはありません.2つ目の理由は、純粋に実用主義のためです.多くのコードが使われているので、それを見たときに認識しなければなりません.実際,enum hackはテンプレートメタプログラミングの基礎技術である.プリプロセッサにフォーカスを戻します.もう1つの一般的な#define誤用は、マクロを実現するためであり、マクロは関数のように見えますが、関数呼び出しによる追加のオーバーヘッドを招くことはありません.以下、このマクロはマクロ実パラメータを挟んで、関数fを呼び出します://aとbの大きな値でfを呼び出します.
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))
このような顔のマクロには多くの欠点があり、それを考えるだけで苦痛になります.いつこのマクロを書いても、マクロのすべての実参に括弧をつけて覚えなければなりません.次の不思議なことを見てみましょう.int a=5、b=0です.CALL_WITH_MAX(++a,b); CALL_WITH_MAX(++a,b+10); ここで,fを呼び出す前に,aのインクリメント回数はなんと「誰と比較されるか」に依存する.幸いなことに、このような退屈なことに温床を提供する必要はありません.マクロがもたらす効率と、一般的な関数のすべての予想可能な動作とタイプの安全性を得ることができます.template inline関数形式、モデル形式、プログラミング形式のない言語を書く限り.これらの能力と弾力性は
template<typename T>
inline void callWithMax(const T& a,const T& b)
{
f(a>b?a:b);
}
template , ,
者はfを呼び出す.ここでは,関数本体にパラメータに括弧を付ける必要もなく,パラメータの計算(評価)を何度も心配する必要もない.......などである.さらにcallWithMaxは真の関数であるため、役割ドメインとアクセスルールを遵守します.たとえば、「class内のprivate inline関数」を絶対に書くことができます.一般的にマクロはこの時点で完了できません.consts、enums、inlinesがあれば、プリプロセッサ(特に#define)の需要は低下しましたが、完全に解消されたわけではありません.includeは依然として必需品であり、#ifdef/#ifndefもコンパイルを制御する重要な役割を果たし続けている.プリプロセッサが完全に引退するまではありませんが、より長い休暇を明確に与える必要があります.単純定数では、#definesをconstオブジェクトまたはenumsで置換することが望ましいが、類似関数のマクロでは、#definesをinline関数で置換することが望ましい