C++安全編碼:斷言

斷言

簡介

在程序設計中,斷言(assertion)是一種放在程序中的一階邏輯(如一個結果爲真或是假的邏輯判斷式),目的是爲了標示與驗證程序開發者預期的結果-當程序運行到斷言的位置時,對應的斷言應該爲真。若斷言不爲真時,程序會中止運行,並給出錯誤消息。

C++斷言包含運行期檢查斷言、靜態斷言

  • 運行期檢查的斷言
    在程序運行時,可以用斷言檢查程序開發時的假設,確認這些假設是否成立。
    存在先天的缺點:可能有改變存儲器數據或是線程時序的風險,因此需小心的處理斷言,確認斷言在程序中沒有其他的副作用。
  • 靜態斷言(c++11以上,static_assert)
    只在編譯期間檢查的斷言稱爲靜態斷言,靜態斷言必需配合清楚的註解說明。
    介紹static_assert

程序結構中的斷言有助於在不使用第三方程式庫的情形下,應用測試驅動開發的開發方式。

示例

運行時期檢查的斷言

  • 頭文件:
所需引用頭文件 基本用法
assert.h(C語法)、cassert(C++) assert(bool_constexpr)

assert.h與cassert : 簡而言之,#include<assert.h>是c語言引用庫的寫法,C++爲了兼容C語言也可以這麼寫,但是可能缺少C++庫的某些特性(比如c99中中的assert.h沒有static_assert宏,C++14的cassert中包含);編寫C++代碼時,最好還是選擇#include 的寫法。

規則 詳細
規則一 斷言必須使用宏定義,禁止直接調用系統提供的assert()
規則二 運行時可能會導致的錯誤,嚴禁使用斷言
規則三 嚴禁在斷言內改變運行環境
規則四 不要將多條語句放在同一個斷言中(無法知道到底是哪個條件出錯)

規則二詳細說明:
環境等外界因素導致的錯誤,不能使用斷言進行判斷;斷言屬於契約式設計中的前置條件,任何斷言失敗,一定標識着某處代碼的錯誤,需要更改代碼,類比編譯錯誤;
如果代碼書寫完全正確,但因外界環境或者用戶操作仍然可能發生的事件,都不適合用斷言;請使用條件判斷或異常進行處理。
錯誤示例:

FILE *fp = fopen(path,"r");
ASSERT(fp != NULL); //文件有可能打開失敗

斷言使用示例:
包含頭文件assert_self.h、main.cpp

/**********************************************************************
      >File Name:     assert_self.h
      >Author:        sunrise
      >Mail:          [email protected]
      >Created Time:  Fri 08 Mar 2019 04:52:55 AM EST
*************************************************************/

#include<iostream>
#include<cassert>
using namespace std;
#ifndef ASSERT_SELF_H
#define ASSERT_SELF_H

#ifdef DEBUG // 定義宏變量,來控制程序是否打開斷言
#define ASSERT(f) assert(f)
#else
#define ASSERT(f) ((void)0)
#endif

#endif

/**********************************************************************
      >File Name:     main.cpp
      >Author:        sunrise
      >Mail:          [email protected]
      >Created Time:  Fri 08 Mar 2019 04:56:03 AM EST
*************************************************************/

#include<iostream>
#include "assert_self.h"
using namespace std;

int main()
{
    int i = 1;
    ASSERT(i > 2);
    cout << "success !!!" << endl;
    return 0;
}

通過宏在編譯階段進行控制(規則一)
打開ASSERT宏:
編譯命令(使用-D 定義宏):

g++ main.cpp assert_self.h --std=c++11 -D DEBUG -o assert

執行結果,斷言生效:

assert: main.cpp:15: int main(): Assertion `i > 2’ failed.
Aborted (core dumped)

關閉ASSERT宏,正常編譯即可
編譯命令:

g++ main.cpp assert_self.h --std=c++11 -o assert

執行結果:

success !!!

靜態斷言

C11 (程序標準版本)及C++11可以用static_assert支持靜態斷言。

示例:

/**********************************************************************
      >File Name:     test.cpp
      >Author:        sunrise
      >Mail:          [email protected]
      >Created Time:  Fri 08 Mar 2019 03:15:51 AM EST
*************************************************************/

#include<iostream>
using namespace std;

#include <type_traits>

template <class T>
void swap1(T& a, T& b)
{
    static_assert(std::is_copy_constructible<T>::value,"Swap requires copying");
    static_assert(std::is_nothrow_copy_constructible<T>::value
                && std::is_nothrow_copy_assignable<T>::value,
                "Swap requires nothrow copy/assign");
    auto c = b;
    b = a;
    a = c;
}

template <class T>
struct data_structure
{
    static_assert(std::is_default_constructible<T>::value,
                "Data Structure requires default-constructible elements");
};

struct no_copy
{
    no_copy ( const no_copy& ) = delete;
    no_copy () = default;
};

struct no_default
{
    no_default () = delete;
};

int main()
{
    const int i = 2;
    static_assert(i > 3,"i should large than 3");

    int a, b;
    swap1(a, b);

    no_copy nc_a, nc_b;
    //swap1(nc_a, nc_b); // 1

    data_structure<int> ds_ok;
    data_structure<no_default> ds_error; // 2
    return 0;
}

備註:std::is_copy_constructible::value這些函數是庫函數,判斷函數構造函數

編譯命令:

g++ test.cpp --std=c++11 -o test

執行結果:

test.cpp: In function ‘int main()’:
test.cpp:47:5: error: static assertion failed: i should large than 3
     static_assert(i > 3,"i should large than 3");
     ^
test.cpp: In instantiation of ‘struct data_structure<no_default>’:
test.cpp:55:32:   required from here
test.cpp:29:5: error: static assertion failed: Data Structure requires default-constructible elements
     static_assert(std::is_default_constructible<T>::value,
     ^
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章