矯情的C++——不明確行為(undefined behavior)

Inside_Zhang發表於2015-11-13

1. 使用未經初始化的(尤其是基本型別)區域性變數

void foo()
{
    int a;
    ++a;        // 錯誤,使用了未經初始化的區域性變數
}
void foo()
{
    int* p = 0;
    std::cout << *p;    // 提領一個null指標,將會導致不明確行為
}

基本型別不像類型別,會隱式地呼叫其預設建構函式,更詳細的內容參見

void foo()
{
    int a = int();      
            // == (int a = 0;), 
            // 使用型別初始化的形式是為了保持基本型別和類型別形式上的統一,尤其在涉及到模板時
}

2. 當derived class 物件經由一個base class指標刪除,而該base class帶著一個non-virtual 解構函式

這裡最好記住幾個結論:
- 如果class不含virtual函式,通常表示它並不意圖被用作一個base class
- 當一個class不企圖被當做base class,令其虛構函式為virtual往往是個壞主意
- 任何class只要帶有virtual函式(客製化)都幾乎確定應該也有一個virtual 解構函式
- 只有當class內至少函一個virtual函式,就應當為其宣告virtual解構函式

3. transform 兩個輸入區間的長度不一致時

在STL的演算法(<algorithm>)實現中,一般針對某一函式(也即是某一特定演算法)都有兩個版本(或者叫函式過載),一種是基礎版本,一種是泛化版本。

transform演算法的遍歷基準是第一輸入序列。

template<typename InputIterator, typename OutputIterator, 
                        typename UnaryOperation>
OutputIterator transform(InputIterator first, InputIterator last, 
                        OutputIterator resutl, UnaryOperation op)
{
    for (; first != last; ++first, ++result)
    {
        // 這裡要保證OutputIterator的長度不能低於InputIterator的長度
        *result = op(*first);
    }
    return result;
}

template<typename InputIterator, typename OutputIterator, 
            typename BinaryOperation>
OutputIterator transform(InputIterator first1, InputIterator last1,
            InputIterator first2, InputIterator last2, 
            BinaryOperation binary_op)
{
    for(; first1 != last1; ++first1, ++first2, ++result)    
    {
        // 這裡不僅要保證輸出序列的長度不能低於第一個輸入序列與的長度
        // 而且要保證第二個輸入序列的長度不能低於第一個輸入序列的長度
        *result = binary_op(*first1, *first2);
    }
    return result;
}
  • 版本1和版本2都會存在的undefined behavior是:

    輸出序列(OutputIterator)的有效長度低於輸入序列(InputIterator)的長度

  • 版本2可能出現的undefined behavior:

    第二個輸入序列(InputIterator 2) 的長度低於第一個輸入序列(InputIterator 1)的長度

這裡僅要求輸出序列的長度不低於輸入序列的長度,而不要求嚴格相等。

4. static_cast 執行向下型別轉換時

class B
{
public:
    virtual void foo() {}
};

class D :public B
{};

當父類指標指向子類物件時,使用static_cast是沒有問題的:

B* b = new D;
D* d1 = static_cast<D*>(b);
D* d2 = dynamic_cast<D*>(b);

當父類指標指向父類物件時,使用static_cast存在安全問題的,或者再使用static_cast進行強制向下型別轉換就會出現undefined behavior

B* b = new B;
D* d1 = static_cast<D*>(b);
                    // 使用 d1 訪問屬於D的成員時,即為undefined behavior

D* d2 = dynamic_cast<D*>(b);
                    // 返回 nullptr

相關文章