C++解析(21):四個操作符
阿新 • • 發佈:2018-12-08
0.目錄
1.邏輯操作符的陷阱
2.逗號操作符的分析
3.前置操作符和後置操作符
4.小結
1.邏輯操作符的陷阱
邏輯運算子的原生語義:
運算元只有兩種值(true和false)
邏輯表示式不用完全計算就能確定最終值
最終結果只能是true或者false
示例——短路法則:
#include <iostream> #include <string> using namespace std; int func(int i) { cout << "int func(int i) : i = " << i << endl; return i; } int main() { if( func(0) && func(1) ) { cout << "Result is true!" << endl; } else { cout << "Result is false!" << endl; } cout << endl; if( func(0) || func(1) ) { cout << "Result is true!" << endl; } else { cout << "Result is false!" << endl; } return 0; }
執行結果為:
[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out
int func(int i) : i = 0
Result is false!
int func(int i) : i = 0
int func(int i) : i = 1
Result is true!
邏輯操作符可以過載嗎?過載邏輯操作符有什麼意義?
示例——過載邏輯操作符:
#include <iostream> #include <string> using namespace std; class Test { int mValue; public: Test(int v) { mValue = v; } int value() const { return mValue; } }; bool operator && (const Test& l, const Test& r) { return l.value() && r.value(); } bool operator || (const Test& l, const Test& r) { return l.value() || r.value(); } Test func(Test i) { cout << "Test func(Test i) : i.value() = " << i.value() << endl; return i; } int main() { Test t0(0); Test t1(1); if( func(t0) && func(t1) ) { cout << "Result is true!" << endl; } else { cout << "Result is false!" << endl; } cout << endl; if( func(1) || func(0) ) { cout << "Result is true!" << endl; } else { cout << "Result is false!" << endl; } return 0; }
執行結果為:
[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out
Test func(Test i) : i.value() = 1
Test func(Test i) : i.value() = 0
Result is false!
Test func(Test i) : i.value() = 0
Test func(Test i) : i.value() = 1
Result is true!
問題的本質分析:
- C++通過函式呼叫擴充套件操作符的功能
- 進入函式體前必須完成所有引數的計算
- 函式引數的計算次序是不定的
- 短路法則完全失效
邏輯操作符過載後無法完全實現原生的語義!
一些有用的建議:
- 實際工程開發中避免過載邏輯操作符
- 通過過載比較操作符代替邏輯操作符過載
- 直接使用成員函式代替邏輯操作符過載
- 使用全域性函式對邏輯操作符進行過載
2.逗號操作符的分析
逗號操符( , )可以構成逗號表示式:
- 逗號表示式用於將多個子表示式連線為一個表示式
- 逗號表示式的值為最後一個子表示式的值
- 逗號表示式中的前N-1個子表示式可以沒有返回值
- 逗號表示式按照從左向右的順序計算每個子表示式的值
示例——逗號表示式之坑:
#include <iostream>
using namespace std;
void func(int i)
{
cout << "func() : i = " << i << endl;
}
int main()
{
int a[3][3] = {
(0, 1, 2),
(3, 4, 5),
(6, 7, 8)
};
int i = 0;
int j = 0;
while( i < 5 )
func(i),
i++;
for(i=0; i<3; i++)
{
for(j=0; j<3; j++)
{
cout << a[i][j] << '\t';
}
cout << endl;
}
(i, j) = 6;
cout << "i = " << i << endl;
cout << "j = " << j << endl;
return 0;
}
執行結果為:
[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out
func() : i = 0
func() : i = 1
func() : i = 2
func() : i = 3
func() : i = 4
2 5 8
0 0 0
0 0 0
i = 3
j = 6
過載逗號操作符:
- 在 C++ 中過載逗號操作符是合法的
- 使用全域性函式對逗號操作符進行過載
- 過載函式的引數必須有一個是類型別
- 過載函式的返回值型別必須是引用
示例——過載逗號操作符:
#include <iostream>
using namespace std;
class Test
{
int mValue;
public:
Test(int i)
{
mValue = i;
}
int value()
{
return mValue;
}
};
Test& operator , (const Test& a, const Test& b)
{
return const_cast<Test&>(b);
}
Test func(Test& i)
{
cout << "func() : i = " << i.value() << endl;
return i;
}
int main()
{
Test t0(0);
Test t1(1);
Test tt = (func(t0), func(t1)); // Test tt = func(t1);
cout << tt.value() << endl; // 1
return 0;
}
執行結果為:
[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out
func() : i = 1
func() : i = 0
1
雖然最終結果是正確的,但是中間過程出錯了!
問題的本質分析:
- C++通過函式呼叫擴充套件操作符的功能
- 進入函式體前必須完成所有引數的計算
- 函式引數的計算次序是不定的
- 過載後無法嚴格從左向右計算表示式
示例——不過載逗號操作符:
#include <iostream>
using namespace std;
class Test
{
int mValue;
public:
Test(int i)
{
mValue = i;
}
int value()
{
return mValue;
}
};
Test func(Test& i)
{
cout << "func() : i = " << i.value() << endl;
return i;
}
int main()
{
Test t0(0);
Test t1(1);
Test tt = (func(t0), func(t1)); // Test tt = func(t1);
cout << tt.value() << endl; // 1
return 0;
}
執行結果為:
[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out
func() : i = 0
func() : i = 1
1
不過載逗號操作符反而是對的!!!
工程中不要過載逗號操作符!
3.前置操作符和後置操作符
下面的程式碼有沒有區別?為什麼?
下列程式碼中的i++;
和++i;
在彙編程式碼中是一樣的!
int main()
{
int i = 0;
i++;
++i;
return 0;
}
意想不到的事實:
- 現代編譯器產品會對程式碼進行優化
- 優化使得最終的二進位制程式更加高效
- 優化後的二進位制程式丟失了C/C++的原生語義
- 不可能從編譯後的二進位制程式還原C/C++程式
C/C++開發的軟體無法完全反編譯!
++操作符可以過載嗎?如何區分前置++和後置++?
++操作符可以被過載:
- 全域性函式和成員函式均可進行過載
- 過載前置++操作符不需要額外的引數
- 過載後置++操作符需要一個int型別的佔位引數
示例——過載++操作符:
#include <iostream>
using namespace std;
class Test
{
int mValue;
public:
Test(int i)
{
mValue = i;
}
int value()
{
return mValue;
}
Test& operator ++ ()
{
++mValue;
return *this;
}
Test operator ++ (int)
{
Test ret(mValue);
mValue++;
return ret;
}
};
int main()
{
Test t(0);
cout << "t.value() = " << t.value() << endl;
Test tt = t++;
cout << "t.value() = " << t.value() << endl;
cout << "tt.value() = " << tt.value() << endl;
tt = ++t;
cout << "t.value() = " << t.value() << endl;
cout << "tt.value() = " << tt.value() << endl;
return 0;
}
執行結果為:
[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out
t.value() = 0
t.value() = 1
tt.value() = 0
t.value() = 2
tt.value() = 2
真正的區別:
- 對於基礎型別的變數
- 前置++的效率與後置++的效率基本相同
- 根據專案組編碼規範進行選擇
- 對於類型別的物件
- 前置++的效率高於後置++
- 儘量使用前置++操作符提供程式效率
4.小結
- C++從語法上支援邏輯操作符過載
- 過載後的邏輯操作符不滿足短路法則
- 工程開發中不要過載邏輯操作符
- 通過過載比較操作符替換邏輯操作符過載
- 通過專用成員函式替換邏輯操作符過載
- 逗號表示式從左向右順序計算每個子表示式的值
- 逗號表示式的值為最後一個子表示式的值
- 操作符過載無法完全實現逗號操作符的原生意義
- 工程開發中不要過載逗號操作符
- 編譯優化使得最終的可執行程式更加高效
- 前置++操作符和後置++操作符都可以被過載
- ++操作符的過載必須符合其原生語義
- 對於基礎型別,前置++與後置++的效率幾乎相同
- 對於類型別,前置++的效率高於後置++