斷言
簡介
在程序設計中,斷言(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,
^