1、運行時斷言
1.1、assert屬於運行時斷言,可以在運行時判斷給定條件是否為真,如果為真則什么也不做,否則打印一跳錯誤信息,然后通過abort來終止程。當程序開發階段,我們可以在debug模式下加入大量的運行時斷言,來提高我們程序的健壯性,並且提高開發速度。但是當程序需要發布的時候,大量的斷言會影響程序的運行效率,此時我們只需要在assert.h頭文件前加#define NDEBUG.
1.2、斷言有一個問題,就是一定會abort,強制整個程序退出而導致調試也無法繼續進行,就像上圖這樣,出現問題后,我們知道了出現問題的行號,但是我們需要手動在該行的上面設置斷點,重新開始調試才能夠檢查到發生問題時各個變量的狀態。而且,有時問題不是那么容易重現,於是就可能出現沒法重現錯誤再檢查狀態的問題。
所以,我們可以自己寫一個類似的宏來解決這個問題,我們希望在違反斷言時觸發斷點陷阱門中斷而不是調用abort,這樣,在違反斷言時程序會暫停下來,等待程序員來檢查當前的狀態有何異常
#define _ASSERT(x) if (!(x)) __asm {int 3}; //是檢查斷言,然后如果斷言結果為false(0),那么就調用內聯匯編指令int 3陷入調試中斷
1.3、用戶自定義斷言
實現功能:斷言的時候可以支持變量輸出SMART_ASSERTvalue && "Invalid value!")("1")(s);
//頭文件
////////////////////////////////////////////////////////////////////////////////
#include
#include
#include
#include
class Assert
{
public:
//編譯期,SMART_ASSERT宏被替換,斷言被命中時
//1、如果斷言后跟有括號括起來的參數時
//SMART_ASSERT_C和SMART_ASSERT_N將不會被認為是成員,而是SMART_ASSERT_C(x)和SMART_ASSERT_N(x)宏
//間接替換為調用ShowMemoryValue成員方法。
//2、如果斷言命中時后邊無括號,則SMART_ASSERT_C被認為是成員變量;
//如果斷言命中時后邊括號在多次宏替換之后沒有啦,則SMART_ASSERT_OP宏被替換后的最后一個.調用被認為是成員變量調用
Assert & SMART_ASSERT_C;
Assert & SMART_ASSERT_N;//SMART_ASSERT_C和SMART_ASSERT_N循環調用,來解析斷言后邊追加的參數
Assert( const wchar_t *wexpr
, const wchar_t *wfile
, const char * expr
, const char * file
, unsigned line)
: _Expr(wexpr)
, _File(wfile)
, _Line(line)
, _CFile(file)
, _Message(expr)
, SMART_ASSERT_C(*this)
, SMART_ASSERT_N(*this)
{
};
~Assert();
//如果需要支持打印多種數據類型,可以對該方法的第二個參數重載,或者對SMART_ASSERT_OP宏的進行修改,傳入參數x時強制轉換
為string類型
Assert & RestoreMemoryValue(const char * key, const std::string &val);
private:
std::wstring _Expr;//異常表達式
std::wstring _File;//文件名稱
unsigned _Line;//文件行數
std::string_CFile;//文件名稱
std::string_Message;//異常消息
std::map _memoryValue;//內存變量值
};
#ifdef SMART_ASSERT
#undef SMART_ASSERT
#endif
#define SMART_ASSERT_C(x)SMART_ASSERT_OP(x, N)
#define SMART_ASSERT_N(x)SMART_ASSERT_OP(x, C)
#define SMART_ASSERT_OP(x, next) \
SMART_ASSERT_C.RestoreMemoryValue(#x, (x)).SMART_ASSERT_##next
#define SMART_ASSERT(expr) \
if ( (expr) ); \
else Assert(_CRT_WIDE(#expr), _CRT_WIDE(__FILE__), #expr, __FILE__, __LINE__).SMART_ASSERT_C
//實現
////////////////////////////////////////////////////////////////////
#include
#include "SmartAssert.h"
//https://msdn.microsoft.com/zh-cn/library/9sb57dw4.aspx :_wassert說明
Assert::~Assert()
{
//獲取_memoryValue臨時變量值打印
//進入中斷
_wassert(_Expr.c_str(), _File.c_str(), _Line);
}
Assert & Assert::RestoreMemoryValue(const char *key, const std::string &val)
{
_memoryValue[key] = val;
return *this;
};
2、靜態斷言
在2011年的C++標准中出現了靜態斷言(static_assert)的語法,所謂靜態斷言,就是在編譯時就能夠進行檢查的斷言,static_assert
是C++的標准語法,不需要引用頭文件。靜態斷言的另一個好處是,可以自定義違反斷言時的編譯錯誤信息。
例如:
const int i = 22;
static_assert(i != 22, "i equals to 22");//這個代碼,將無法通過編譯,因為i的值違反了靜態斷言。
注意:靜態斷言的限制是,斷言本身必須是常量表達式,如果這樣的i不是常量,靜態斷言是不符合語法的。