1. 程式人生 > >C++解析(21):四個操作符

C++解析(21):四個操作符

0.目錄

1.邏輯操作符的陷阱

2.逗號操作符的分析

3.前置操作符和後置操作符

4.小結

1.邏輯操作符的陷阱

邏輯運算子的原生語義:
運算元只有兩種值(truefalse
邏輯表示式不用完全計算就能確定最終值
最終結果只能是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!

問題的本質分析

  1. C++通過函式呼叫擴充套件操作符的功能
  2. 進入函式體前必須完成所有引數的計算
  3. 函式引數的計算次序是不定的
  4. 短路法則完全失效

邏輯操作符過載後無法完全實現原生的語義!

一些有用的建議:

  • 實際工程開發中避免過載邏輯操作符
  • 通過過載比較操作符代替邏輯操作符過載
  • 直接使用成員函式代替邏輯操作符過載
  • 使用全域性函式對邏輯操作符進行過載

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

雖然最終結果是正確的,但是中間過程出錯了!

問題的本質分析

  1. C++通過函式呼叫擴充套件操作符的功能
  2. 進入函式體前必須完成所有引數的計算
  3. 函式引數的計算次序是不定的
  4. 過載後無法嚴格從左向右計算表示式

示例——不過載逗號操作符:

#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

真正的區別:

  • 對於基礎型別的變數
    1. 前置++的效率與後置++的效率基本相同
    2. 根據專案組編碼規範進行選擇
  • 對於類型別的物件
    1. 前置++的效率高於後置++
    2. 儘量使用前置++操作符提供程式效率

4.小結

  • C++從語法上支援邏輯操作符過載
  • 過載後的邏輯操作符不滿足短路法則
  • 工程開發中不要過載邏輯操作符
  • 通過過載比較操作符替換邏輯操作符過載
  • 通過專用成員函式替換邏輯操作符過載
  • 逗號表示式從左向右順序計算每個子表示式的值
  • 逗號表示式的值為最後一個子表示式的值
  • 操作符過載無法完全實現逗號操作符的原生意義
  • 工程開發中不要過載逗號操作符
  • 編譯優化使得最終的可執行程式更加高效
  • 前置++操作符和後置++操作符都可以被過載
  • ++操作符的過載必須符合其原生語義
  • 對於基礎型別,前置++與後置++的效率幾乎相同
  • 對於類型別前置++的效率高於後置++