いつもお世話になります。みなさんに質問させて頂きたいことがあります。
C++において define定義はあまり使うべきではないというのを最近になってしりま
した。その代わりにConstで定義するということでした。
その利点としては Constでは型が明確に記述できるということと安全性を高めるとい
ったことでした。型を明確にするということに関しては理解できるのですが、安全性と
高めるということはどういったことなのでしょうか??define定義には何らかのデメリ
ットも含んでいるのでしょうか??
ご教授よろしくお願いします。
軽く突っ込みを。
Constではなくてconstだと思いますが。
C++では大文字と小文字は区別されるので意味合いが変わってしまうと思います。
型が明確であると言う事自体が安全である事につながっていると思いますけれど。
constで定義すると型が明確になっているのでコンパイル時などに警告が出たり
するので動かしてみる前に危険性があるコードを確認できます。
こういった安全性を型安全性と呼んでいると思います。
あと、#defineだと#undefすれば、定義を局所的に変えられるので
便利ではありますが、その分だけ危険も伴うと思います。
#define はコンパイルの最初のほうのステージで行われる「置換」です。
すなわち、メンバアクセス制限のたぐいを無視して無節操に置換されます。
const はクラスのメンバにすることができ、メンバアクセス制限の対象になります。
class A {
static const int max_size=256;
...
};
とすると A::max_size は private となり、したがって class B の中から使えません。
使えない==間違って使うことが無い==安全ということですね。
#define MAX_SIZE 256
だとこれは class B の中だろうがどこだろうが使えてしまいます。
間違って使えてしまう==危険ということです。
元プログラマの記述が悪い (MAX_SIZE_OF_A とすべきだった) のですが、
これだと何か汎用な「サイズ」とみなされても仕方ない記述になってしまっていて、
より誤使用する確率が高くなってしまった悪い例ということで。
const と関係ないけど
#define sqr(x) x*x はきわめて危険です。
template<class T> inline T sqr(T x) { return x*x; } は安全です。
C++ だと #define は #if 類と組み合わせる目的でのみ使うほうがよいですね。
ほとんどの場合、より安全な代替手段がありますから。
あぁ、こーいう(不注意なプログラマに対する)安全性もあるですね。
#define MAX_SIZE 12+34
const size_t max_size=12+34;
#define は単純に置換されるだけなので次のような使い方をするとバグります。
int data[MAX_SIZE*2]; // int data[12+34*2]; に置換される
プログラマはおそらく (12+34)*2 を期待しているのに実際の配列の個数は違います。
int data[max_size*2]; だと置換ではなくコンパイル時評価になるので正しく動作します。
この例ではプログラマが注意して #define MAX_SIZE (12+34) と書けばよかったのですが
不注意なプログラマが括弧を付け忘れるとバグってしまうわけです。
PATIOさん。tetrapod さん。ご丁寧にありがとうございます。
PATIOさんとtetrapodさんからご教授頂いたことをしっかり理解して励んでいきたいと思
います。本当にありがとうございました。また何かありましたらよろしくお願いいたします。
tetrapodさん>
# 間違っていたらご指摘願います。
> const size_t max_size=12+34;
> int data[max_size*2]; だと置換ではなくコンパイル時評価になるので正しく動作します。
これって、実行時ではないのですか?
私はよく、enumで回避していますけども。(それともコンパイラが古いから?<VC++6)
enum { max_size = 12+34 };
int data[max_size*2];
これならコンパイルエラーになりません。
うん?別にエラーになりませんよ。二番目の例はクラスメンバにしたつもりはないし。
#define に対比させる目的で、地の (非メンバの) const int を使ったつもり。
クラス内 static const メンバがうまく初期化できないのは VC++6 のバグ
static const int max_size=12+34;
int main() {
volatile int data[max_size*2]; data[0]=0;
return sizeof data;
}
は gcc/bcc/vc++6/shc/ch38 のどれでも期待通りになりました。
# gcc が賢いというかバスアライメントまで考えてるというか、メモリが無駄というか...
>これって、実行時ではないのですか?
配列の定義においては要素数は値が0以上の汎整数定数式でなければならない (8.3.4 Arrays)
定数式とは ... (5.19)
5.19 を読解すると、コンパイル時点で値評価可能なもののみ定数というらしい。
だから「先に挙げた例題では」コンパイル時点評価でなければならないようです。
# gcc などが実行時評価式で配列を作れるのは独自拡張。
tetrapodさんが問題にしておられるのは、算術演算上の順番に起因する部分ですよね。
書き手が(12+24)*2を期待しているにも拘らず、展開結果が12+24*2となってしまう為に
演算結果に差が出てしまうという話だと思います。
コンパイルエラーと言う話ではないですね。