=====C++ 演算子の優先順位===== リストの上位の演算子から順番に評価されます。同じグループの演算子は同じ優先度を持っています。すべての演算子は、特別な情報がない限りは、左から右への結合性があります。 \\ \\ \\ \\ \\ \\ ^ 優先度 ^ 演算子 ^ 説明 ^ サンプル ^ オーバーロード可能か? ^ 結合性 ^ |1|''::''|スコープ解決演算子|Class::age = 2;|no|none| |2|''()''\\ ''()''\\ ''[]''\\ ''%%->%%''\\ ''.''\\ ''++''\\ ''%%--%%''\\ ''const_cast''\\ ''dynamic_cast''\\ ''static_cast''\\ ''reinterpret_cast''\\ ''typeid''|関数呼び出し\\ メンバーの初期化 \\ 配列アクセス\\ ポインタ変数からのメンバーアクセス\\ オブジェクトからのメンバーアクセス\\ 後置インクリメント\\ 後置デクリメント\\ 特別なキャスト\\ 特別なキャスト\\ 特別なキャスト\\ 特別なキャスト\\ 実行時型情報|isdigit('1')\\ c_tor(int x, int y) : _x(x), _y(y*10){};\\ array[4] = 2;\\ %%ptr->age = 34;%%\\ obj.age = 34;\\ %%for( int i = 0; i < 10; i++ ) cout << i;%%\\ %%for( int i = 10; i > 0; i-- ) cout << i;%%\\ const_cast(type_from);\\ dynamic_cast(type_from);\\ static_cast(type_from);\\ reinterpret_cast(type_from);\\ cout << typeid(type).name();|yes\\ yes\\ yes\\ yes\\ no\\ yes\\ yes\\ no\\ no\\ no\\ no\\ no|左から右| |3|''!''\\ ''not''\\ ''~''\\ ''compl''\\ ''++''\\ ''%%--%%''\\ ''-''\\ ''+''\\ ''*''\\ ''&''\\ ''new''\\ ''new []''\\ ''delete''\\ ''delete []''\\ ''(type)''\\ ''sizeof''|論理的反転\\ ! の別名 \\ ビットの比較\\ ~ の別名 \\ 前置インクリメント\\ 前置デクリメント\\ 単項マイナス\\ 単項プラス\\ 参照外し\\ アドレス\\ 動的メモリ割り当て\\ 配列の動的メモリ割り当て\\ メモリのデリート\\ 配列のメモリのデリート\\ 指定された型へのキャスト\\ オブジェクトもしくは型のサイズ取得|if( !done ) ...\\ \\ flags = ~flags;\\ \\ %%for( i = 0; i < 10; ++i ) cout << i;%%\\ %%for( i = 10; i > 0; --i ) cout << i;%%\\ int i = -1;\\ int i = +1;\\ int data = *intPtr;\\ int *intPtr = &data;\\ long *pVar = new long; \\ MyClass *ptr = new MyClass(args);\\ delete pVar;\\ delete [] array;\\ int i = (int) floatNum;\\int size = sizeof(float);|yes\\ \\ yes\\ \\ yes\\ yes\\ yes\\ yes\\ yes\\ yes\\ yes\\ yes\\ yes\\ yes\\ yes\\ no|右から左| |4|''%%->*%%''\\ ''.*''|メンバーポインタのセレクタ\\ メンバーオブジェクトのセレクタ|%%ptr->*var = 24;%%\\ obj.*var = 24;|yes\\ no|左から右| |5|''*''\\ ''/''\\ ''%''|かけ算\\ 割り算\\ 剰余|int i = 2 * 4;\\ float f = 10.0 / 3.0;\\ int rem = 4 % 3;|yes\\ yes\\ yes|左から右| |6|''+''\\ ''-''|足し算\\ 引き算|int i = 2 + 3;\\ int i = 5 - 1;|yes\\ yes|左から右| |7|''%%<<%%''\\ ''%%>>%%''|左ビットシフト\\ 右ビットシフト|%%int flags = 33 << 1;%%\\ %%int flags = 33 >> 1;%%|yes\\ yes|左から右| |8|''<''\\ ''%%<=%%''\\ ''>''\\ ''%%>=%%''|比較 より小さい\\ 比較 より小さいか等しい\\ 比較 より大きい\\ 比較 より大きいか等しい|if( i < 42 ) ...\\ %%if( i <= 42 ) ...%%\\ if( i > 42 ) ...\\ %%if( i >= 42 ) ...%%|yes\\ yes\\ yes\\ yes|左から右| |9|''%%==%%''\\ ''eq''\\ ''!=''\\ ''not_eq''|比較 等しい\\ %%==%% の別名 \\ 比較 等しくない\\ != の別名 |%%if( i == 42 ) ...%%\\ \\ if( i != 42 ) ...\\ |yes\\ -\\ yes\\ |左から右| |10|''&''\\ ''bitand''|ビット積(AND)\\ & の別名|flags = flags & 42;\\ |yes\\ |左から右| |11|''%%^%%''\\ ''xor''|ビット排他的論理和(XOR)\\ %%^%% の別名 |%%flags = flags ^ 42;%%\\ |yes\\ |左から右| |12|''%%|%%''\\ ''bitor''|ビット和(OR)\\ %%|%% の別名|%%flags = flags | 42;%%\\ |yes\\ |左から右| |13|''&&''\\ ''and''|論理積(AND)\\ && の別名|if( conditionA && conditionB ) ...\\ |yes\\ |左から右| |14|''%%||%%''\\ ''or''|論理和(OR)\\ %%||%% の別名|%%if( conditionA || conditionB ) ...%%\\ |yes\\ |左から右| |15|''? :''|Ternary conditional (if-then-else)|int i = (a > b) ? a : b;|no|right to left| |16|''=''\\ ''+=''\\ ''-=''\\ ''*=''\\ ''/=''\\ ''%=''\\ ''&=''\\ ''and_eq''\\ ''%%^=%%''\\ ''xor_eq''\\ ''%%|=%%''\\ ''or_eq''\\ ''%%<<=%%''\\ ''%%>>=%%''|代入演算子\\ 足して代入\\ 引いて代入\\ 積の代入\\ 商の代入\\ 剰余の代入\\ ビット積(AND)の代入\\ &= の別名 \\ ビット排他的論理和(XOR)の代入\\ %%^=%% の代入 \\ ビット和(OR)の代入\\ %%|=%% の別名\\ 左ビットシフトの代入\\ 右ビットシフトの代入|int a = b;\\ a += 3;\\ b -= 4;\\ a *= 5;\\ a /= 2;\\ a %= 3;\\ flags &= new_flags;\\ \\ %%flags ^= new_flags;%%\\ \\ %%flags |= new_flags;%%\\ \\ %%flags <<= 2;%%\\ %%flags >>= 2;%%|yes\\ yes\\ yes\\ yes\\ yes\\ yes\\ yes\\ \\ yes\\ \\ yes\\ \\ yes\\ yes|右から左| |17|''throw''|例外のスロー|throw EClass("Message");|no||| |18|'',''|逐次評価演算子|for( i = 0, j = 0; i < 10; i++, j++ ) ...|yes|左から右| =====評価順序と副作用===== 演算子の優先順位に関して、C++の重要な側面の一つは、式における評価順序と、副作用の順序です。いくつかの場面ではどのような結果になるか未定義なものがあります。例えば、以下のコードを見てください: float x = 1; x = x / ++x; コンパイラが先に評価するのが、割り算の左側か、それとも右側かというのは明確に仕様として決まっていません。そのため、xの値は異なるコンパイラを使っても同じ結果になるという保証はされません。どちらが先に評価されるかによって、xの値は変化します。 それに加えて、++xがx+1と評価されて実際に格納されて副作用が発生するタイミングも不定です。そのため、タイミングごとに違うxの値として評価されることがあるため、最終的なxの値が異なる可能性があります。 結論としては、上記のような式は恐ろしく曖昧なため、どのようなコストを支払っても避けるべきです。疑わしい場合には、一つの曖昧な式を複数の式に分割し、評価順序が明確になるようにしてください。 =====演算子のオーバーロード===== 演算子のオーバーロードはとても便利に使用できる反面、とても危険な状態になる可能性もあります。便利な側面としては、自分で作成したクラスの演算子をオーバーロードすることで、既存の演算子の知識を利用できるようになって読みやすさが向上するという面があります。もう一方の危険な側面としては、予想外の評価がされてしまってプログラムの動作が違うものになってしまうか、最悪の場合はプログラムを壊すことにもなりかねません。慎重に使用してください。 演算子のオーバーロードをするには、グローバル関数として定義する方法と、クラスのメンバーとして定義する方法の2種類あります。 グローバル関数として定義する場合のサンプル: ostream & operator<< (ostream & os, const myClass & rhs); ユーザが定義したクラスの中のプライベートなデータにアクセスする場合には、クラス宣言の中で、上記のグローバル関数をfriend関数として宣言する必要があります。 例: class myClass { // operator<<関数が'myData'にアクセスできるようにします // (この宣言はpublicではなく、privateかprotectedにすべきです) friend ostream & operator<< (ostream & lhs, const myClass & rhs); private: int myData; } クラスのメンバーとして演算子のオーバーロードを行うには、以下のようにします: class myClass { public: // 演算子の左辺値は'this'ポインタになります int operator+ (const myClass & rhs); private: int myData; }