C++之異常處理

C語言傳統的處理錯誤的方式

傳統的錯誤處理機制:

1. 終止程序, 如assert.

缺陷: 用戶難以接受, 如發生內存錯誤, 除0錯誤時就會終止程序.

如果assert括號里面的表達式結果為假, 那么assert就會中斷程序并報錯, 所以使用assert可以幫助我們在程序判斷一些可能出錯的地方.

#include <assert.h>
int main()
{int x, y;cin >> x >> y;assert(y != 0);cout << x / y << endl;return 0;
}


2. 返回錯誤碼,?缺陷: 需要程序員自己去查找對應的錯誤, 如系統的很多庫的接口函數都是通
過把錯誤碼放到errno中, 表示錯誤.

實際中C語言基本都是使用返回錯誤碼的方式處理錯誤, 部分情況下使用終止程序處理非常嚴重的錯誤。


c++異常的使用

異常是一種處理錯誤的方式,?當一個函數發現自己無法處理的錯誤時就可以拋出異常, 讓函數的
直接或間接的調用者處理這個錯誤。

c++處理錯誤需要三個關鍵字 try, throw, catch.

???????throw: 當問題出現時, 程序會拋出一個異常, 這個異常對象可以是任意的類型
catch: 在想要處理問題的地方, 通過異常處理程序捕獲異常. catch 關鍵字用于捕獲異
常, 可以有多個catch進行捕獲.?
try: try 塊中的代碼標識將被激活的特定異常,它后面通常跟著一個或多個 catch 塊.

如果有一個塊拋出一個異常, 捕獲異常的方法會使用 try 和 catch 關鍵字. try 塊中放置可能拋
出異常的代碼, try 塊中的代碼被稱為保護代碼. 使用 try/catch 語句的語法如下所示:

try
{// 保護的標識代碼
}catch( ExceptionName e1 )
{// catch 塊
}catch( ExceptionName e2 )
{// catch 塊
}catch( ExceptionName eN )
{// catch 塊
}

首先我們有個名為func函數, 在函數里面我們要接收兩個參數, 并打印這兩個參數相除的結果, 那么這個時候我們就再創建一個函數來實現兩個參數相除, 并將相除的結果進行返回:?

double Division(int a, int b)
{// 當b == 0時拋出異常if (b == 0)throw "Division by zero condition!";elsereturn ((double)a / (double)b);
}void Func()
{int len, time;cin >> len >> time;cout << Division(len, time) << endl;
}int main()
{try {Func();}  catch (const char* errmsg){cout << errmsg << endl;}return 0;
}

1. 當我們發現異常之后就得使用throw來拋出異常, 那這里拋出的異常對象我們就可以使用字符串, 字符串的內容為:?Division by zero condition!

2. 函數寫完之后就可以在main函數里面進行Func函數來運行我們寫的函數, 又因為這個函數里面可能會拋出異常, 所以我們把這個Func函數放到try塊里面.

3. 在try塊里面拋出了異常就得在try塊的外面使用catch來進行接收, 因為拋出的是字符串類型的異常對象, 所以在使用catch接收的時候就得在catch后面的括號中寫入字符串類型的參數來進行接收,在catch對應的語句塊里就可以把對應的錯誤提示輸出到屏幕上面.


異常的規則?

異常的拋出和匹配原則

1. 異常是通過拋出對象而引發的, 該對象的類型決定了應該激活哪個catch的處理代碼.

double Division(int a, int b)
{// 當b == 0時拋出異常if (b == 0)throw "Division by zero condition!";elsereturn ((double)a / (double)b);}void Func()
{int len, time;cin >> len >> time;cout << Division(len, time) << endl;
}int main()
{try{Func();}catch (const char c){cout << c << endl;}/*catch (const char* errmsg){cout << errmsg << endl;}*/return 0;
}

?假如將catch的參數換成char類型, 和拋出對象不匹配.

不會調用catch, 會像之前一樣直接終止程序.


2. 被選中的處理代碼是調用鏈中與該對象類型匹配且離拋出異常位置最近的那一個. 在同一個作用域里面不能有相同有相同類型的catch語句, 但是在不同的作用域里面可以存在相同的catch語句.

double Division(int a, int b)
{// 當b == 0時拋出異常if (b == 0)throw "Division by zero condition!";elsereturn ((double)a / (double)b);}void Func()
{int len, time;cin >> len >> time;try{cout << Division(len, time) << endl;}catch (const char* errmsg){cout <<"我是離throw最近的catch: " << errmsg << endl;}
}int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}return 0;
}

?

這個"近"的概念和catch的匹配原則有關, 如果執行了throw, 會先在本函數(Division)里檢查throw是否在try里, 如果有就會去匹配對應的catch語句, 有try一定有catch,不然會報錯.

如果不在try塊里或者沒找到匹配的catch, 就會退出當前函數棧幀(Division), 繼續到調用這個函數的函數棧幀(func)中去尋找catch, 找不到就繼續遞歸尋找, 如果main函數里也沒有匹配的catch直接終止程序. 上面的例子就是在throw不在try塊里, 所以去調用Division的函數里找catch并找到了匹配的catch.

雖然catch語句可以存在多個, 但是拋出的異常在被捕捉的時候只能被一個catch語句所捕捉, 一旦一個catch語句捕捉成功了, 那么其他的catch語句都不會被執行, 所以異常是通過拋出對象而引發的, 該對象的類型決定了應該激活哪個catch的處理代碼.

當catch語句執行完之后還可以繼續執行后面的內容:??

double Division(int a, int b)
{try {// 當b == 0時拋出異常if (b == 0)throw "Division by zero condition!";elsereturn ((double)a / (double)b);}catch (const char* errmsg){cout<< "我是離throw最近的catch: " << errmsg << endl;}cout << "捕捉異常后, 我會繼續執行" << endl;
}void Func()
{int len, time;cin >> len >> time;try{cout << Division(len, time) << endl;}catch (const char* errmsg){cout << errmsg << endl;}
}int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}return 0;
}

throw拋出異常后如果沒有找到catch后面的語句會正常執行嗎?

double Division(int a, int b)
{// 當b == 0時拋出異常if (b == 0)throw "Division by zero condition!";elsereturn ((double)a / (double)b);cout << "異常拋出后我會執行嗎" << endl;
}void Func()
{int len, time;cin >> len >> time;try{cout << Division(len, time) << endl;}catch (const char c){}cout << "異常拋出后我會執行嗎" << endl;
}int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}return 0;
}

可以看到division和func里都沒有catch或者匹配的catch, 后面的語句都沒有執行.

注意, 上面說的catch語句執行完之后還可以繼續執行后面的內容是指已經catch結束了會執行后面的語句, 這里還是處于異常拋出階段, 是在匹配catch.

那我在func里事先定義一個對象, 沒有匹配的catch的話會調用析構嗎??

class A
{
public:A(){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
};double Division(int a, int b)
{// 當b == 0時拋出異常if (b == 0)throw "Division by zero condition!";elsereturn ((double)a / (double)b);cout << "捕捉異常后, 我會繼續執行" << endl;
}void Func()
{int len, time;cin >> len >> time;A a;try{cout << Division(len, time) << endl;}catch (const char c){}cout << "異常拋出后我會執行嗎" << endl;
}int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}return 0;
}

?析構還是會調用的, 因為它雖然是一個棧幀一個棧幀去找catch, 但是沒找到的話棧幀還是會銷毀的.?

在函數調用鏈中異常棧展開匹配原則

1. 首先檢查throw本身是否在try塊內部, 如果是再查找匹配的catch語句. 如果有匹配的, 則
調到catch的地方進行處理.?
2. 沒有匹配的catch則退出當前函數棧, 繼續在調用函數的棧中進行查找匹配的catch.
3. 如果到達main函數的棧, 依舊沒有匹配的, 則終止程序, 上述這個沿著調用鏈查找匹配的
catch子句的過程稱為棧展開. 所以實際中我們最后都要加一個catch(...)捕獲任意類型的異
常, 否則當有異常沒捕獲, 程序就會直接終止.(下面會說)
4. 找到匹配的catch子句并處理以后,會繼續沿著catch子句后面繼續執行。?


3. 拋出異常對象后, 會生成一個異常對象的拷貝, 因為拋出的異常對象可能是一個臨時對象, 所以會生成一個拷貝對象, 這個拷貝的臨時對象會在被catch以后銷毀.(這里的處理類似于函數的傳值返回)

double Division(int x, int y)
{if (y == 0){string s("除0錯誤");throw s;}else{return (double)x / (double)y;}
}void Func()
{A a;try {int x, y;cin >> x >> y;cout << Division(x, y) << endl;}catch (const char s){cout << s << endl;}
}int main()
{try{Func();}catch (const string& s){cout << s << endl;}return 0;
}

這里拋出異常選擇拋出一個string類型的對象, 但是這里異常拋出的是創建出來的s嗎? 肯定不是, 因為s是一個局部對象, s的生命周期在當前作用域, 出了作用域就會銷毀, 所以會產生一個臨時對象的拷貝, 拷貝什么時候銷毀? catch用完了就會銷毀. 那拷貝的代價會不會很大? 拷貝的代價肯定不小, 但是有了移動構造代價就小了很多, 因為深拷貝的對象如果是右值會調用移動構造, 淺拷貝的對象就要按字節進行拷貝了, 但是淺拷貝的對象一般也不會很大.


4. 寫代碼的時候可能會遇到各種各樣的異常, 如果有一個新的異常沒有被以往的catch捕獲到, 那么最終到main函數還沒捕捉到, 就會程序崩潰, 因為一個小的異常而導致整個項目崩潰, 代價太大.

double Division(int x, int y)
{if (y== 0){string s("除0錯誤");throw s;}else{return (double)x / (double)y;}
}void f1()
{throw 1;
}void Func()
{A a;try {int x, y;cin >> x >> y;cout << Division(x, y) << endl;}catch (const char s){cout << s << endl;}f1();
}int main()
{while (1){try{Func();}catch (const string& s){cout << s << endl;}}return 0;
}

假如我當前的程序是一個循環狀態,??現在在func里寫了一個函數只要運行就拋異常, 而且這個異常還沒辦法被catch, 就會導致程序崩潰.

?那么為了能捕獲所有的異常,?c++提供了一個這樣類型的捕捉:?catch(...), 它可以捕獲任意類型的異常.

int main()
{while (1){try{Func();}catch (const string& s){cout << s << endl;}catch (...)//走到這里說明有人沒有按 規范/約定 來拋異常{cout << "未知錯誤" << endl;}}return 0;
}


5. 實際中拋出和捕獲的匹配原則有個例外, 并不都是類型完全匹配, 可以拋出的派生類對象, 使用基類捕獲, 這個在實際中非常實用.

自定義異常體系?

實際中都會自定義自己的異常體系進行規范的異常管理, 因為一個項目中如果大家隨意拋異常, 那么外層的調用者就沒辦法處理了, 所以實際中都會定義一套繼承的規范體系.

這樣大家拋出的都是繼承的派生類對象, 捕獲一個基類就可以了?

如果每個小組分別負責數據庫模塊, 緩存模塊, 業務模塊, 每個小組都會拋出異常, 但是每個小組拋出的異常類型又不同, 如果一起在main函數里面進行捕捉的話就會非常的多并且非常的復雜, 并且每個小組所拋出的異常都有著自己的模塊的屬性, 所以使用一個類來解決這里的異常就不現實.

所以定義一個基類的異常, 每個小組拋出這個基類的派生類異常, 在main函數里面使用基類來統一捕獲. 比如這里的父類里面有個int類型的錯誤碼和一個string類型的對象來記錄當前錯誤的描述信息, 然后在這個類里面就可以定義一個虛擬函數用于返回內部的string對象以方便使用人員進行打印.

class Exception
{
public:Exception(const string& errmsg, int id):_errmsg(errmsg), _id(id){}virtual string what() const{return _errmsg;}
protected:string _errmsg; // 錯誤描述int _id;		// 錯誤編號
};class SqlException : public Exception
{
public:SqlException(const string& errmsg, int id, const string& sql):Exception(errmsg, id), _sql(sql){}//多態virtual string what() const{string str = "SqlException:";str += _errmsg;str += "->";str += _sql;return str;}private:const string _sql;//數據庫自己的錯誤信息
};class CacheException : public Exception
{
public:CacheException(const string& errmsg, int id):Exception(errmsg, id){}virtual string what() const{string str = "CacheException:";str += _errmsg;return str;}
};class HttpServerException : public Exception
{
public:HttpServerException(const string& errmsg, int id, const string& type):Exception(errmsg, id), _type(type){}virtual string what() const{string str = "HttpServerException:";str += _type;str += ":";str += _errmsg;return str;}private:const string _type;
};void SQLMgr()
{//模擬異常拋出srand(time(0));if (rand() % 7 == 0){throw SqlException("權限不足", 100, "select * from name = '張三'");}}void CacheMgr()
{//模擬異常拋出srand(time(0));if (rand() % 5 == 0){throw CacheException("權限不足", 100);}else if (rand() % 6 == 0){throw CacheException("數據不存在", 101);}SQLMgr();
}void HttpServer()
{// ...srand(time(0));if (rand() % 3 == 0){throw HttpServerException("請求資源不存在", 100, "get");}else if (rand() % 4 == 0){throw HttpServerException("權限不足", 101, "post");}CacheMgr();
}
int main()
{while (1){Sleep(500);try {HttpServer();}catch (const Exception& e) // 這里捕獲父類對象就可以{// 多態cout << e.what() << endl;}catch (...){cout << "Unkown Exception" << endl;}}return 0;
}


異常的重新拋出

有可能單個的catch不能完全處理一個異常, 在進行一些校正處理以后, 希望再交給更外層的調用鏈函數來處理, catch則可以通過重新拋出將異常傳遞給更上層的函數進行處理.

首先看一個場景:??

double Division(int a, int b)
{// 當b == 0時拋出異常if (b == 0){throw "Division by zero condition!";}return (double)a / (double)b;
}void Func()
{// 這里可以看到如果發生除0錯誤拋出異常,另外下面的array沒有得到釋放。int* array1 = new int[10];// 異常安全問題int len, time;cin >> len >> time;cout << Division(len, time) << endl;cout << "delete []" << array1 << endl;delete[] array1;}int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}return 0;
}

Func函數里面沒有捕獲異常, 但是在Func函數里面我們使用new申請了一段空間, 并且在調用Division函數之后使用delete將這段空間給釋放掉了, 這么寫如果沒有拋出異常的話是沒有問題的:?

但是一旦這里拋出了異常那么就會發生內存泄漏:

原因就是Division函數里面發生了異常, throw拋出一個異常之后Func函數里面沒有catch語句來進行接收, 所以就會來到main函數里面被main函數里面的catch所接收, 也就意味著不會執行func函數剩下的代碼, 所以就發生了泄漏, 在之前的學習中我們認為內存泄漏多半是因為編寫代碼的人忘記了使用delete來釋放空間而導致的內存泄漏, 而現在即使寫了delete也有可能因為異常的執行流導致內存泄漏.

解決方法:

所有寫new和delete的地方都要加一個try catch, 并重新拋出異常:

那樣寫比較麻煩, 因為如果要拋出多個異常還要為每個異常重新拋出一次, 所以直接catch(...):

double Division(int a, int b)
{// 當b == 0時拋出異常if (b == 0){throw "Division by zero condition!";}return (double)a / (double)b;
}void Func()
{// 這里可以看到如果發生除0錯誤拋出異常,另外下面的array沒有得到釋放。// 所以這里捕獲異常后并不處理異常,異常還是交給外面處理,這里捕獲了再// 重新拋出去。int* array1 = new int[10];// 異常安全問題try{int len, time;cin >> len >> time;cout << Division(len, time) << endl;}catch(...){cout << "delete []" << array1 << endl;delete[] array1;throw;//捕捉什么拋出什么}cout << "delete []" << array1 << endl;delete[] array1;
}int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}return 0;
}

現在有一個問題, 如果我new了兩個對象呢??

double Division(int a, int b)
{// 當b == 0時拋出異常if (b == 0){throw "Division by zero condition!";}return (double)a / (double)b;
}void Func()
{// 這里可以看到如果發生除0錯誤拋出異常,另外下面的array沒有得到釋放。// 所以這里捕獲異常后并不處理異常,異常還是交給外面處理,這里捕獲了再// 重新拋出去。int* array1 = new int[10];int* array2 = new int[10];// 異常安全問題try{int len, time;cin >> len >> time;cout << Division(len, time) << endl;}catch(...){cout << "delete []" << array1 << endl;delete[] array1;cout << "delete []" << array2 << endl;delete[] array2;throw;//捕捉什么拋出什么}cout << "delete []" << array1 << endl;delete[] array1;cout << "delete []" << array2 << endl;delete[] array2;
}int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}catch (const exception& e){cout << e.what();}return 0;
}

也可以像上面那樣去處理, 但是如果我在new的地方拋了異常呢??如果在new array1的時候拋出了異常, 那么就會匹配catch(const exception& e), 但是如果new array2的時候拋出了異常, array1就無法正常delete了, 解決方法會在智能指針章節介紹.


異常安全?

1. 構造函數完成對象的構造和初始化, 最好不要在構造函數中拋出異常, 否則可能導致對象不完整或沒有完全初始化.

2. 析構函數主要完成資源的清理, 最好不要在析構函數內拋出異常, 否則可能導致資源泄漏(內存泄漏, 句柄未關閉等)
3.C++中異常經常會導致資源泄漏的問題, 比如在new和delete中拋出了異常, 導致內存泄漏, 在lock和unlock之間拋出了異常導致死鎖, C++經常使用RAII來解決以上問題, RAII在智能指針介紹.


異常規范?

1. 異常規格說明的目的是為了讓函數使用者知道該函數可能拋出的異常有哪些.可以在函數的后面接throw(類型), 列出這個函數可能拋擲的所有異常類型.
2. 函數的后面接throw(), 表示函數不拋異常.
3. 若無異常接口聲明, 則此函數可以拋擲任何類型的異常.

// 這里表示這個函數會拋出A/B/C/D中的某種類型的異常
void fun() throw(A,B,C,D);
// 這里表示這個函數只會拋出bad_alloc的異常
void* operator new (std::size_t size) throw (std::bad_alloc);
// 這里表示這個函數不會拋出異常
void* operator delete (std::size_t size, void* ptr) throw();
// C++11 中新增的noexcept,表示不會拋異常
thread() noexcept;
thread (thread&& x) noexcept;

雖然C++98的throw()和C++11的noexcept都代表不會拋異常, 但它們有一點區別, 如果throw()表示的函數內拋出了異常編譯運行都可以通過, 如果noexcept表示的韓式拋出了異常編譯時可以通過, 但是運行就會報錯.?


C++標準庫的異常體系

C++ 提供了一系列標準的異常, 定義在 中. 我們可以在程序中使用這些標準的異常, 它們是以父子類層次結構組織起來的, 如下所示:?

?

說明: 實際中我們可以可以去繼承exception類實現自己的異常類. 但是實際中往往像上面一樣自己定義一套異常繼承體系. 因為C++標準庫設計的不夠好用.


?異常的優缺點

C++異常的優點:

1. 異常對象定義好了, 相比錯誤碼的方式可以清晰準確展示出錯誤的各種信息, 甚至可以包含堆棧調用的信息, 這樣可以幫助更好的定位程序的bug.

2. 返回錯誤碼的傳統方式有個很大的問題就是, 在函數調用鏈中, 深層的函數返回了錯誤, 那么我們得層層返回錯誤, 最外層才能拿到錯誤, 比如:

// 1.下面這段偽代碼我們可以看到ConnnectSql中出錯了,先返回給ServerStart,
//ServerStart再返回給main函數,main函數再針對問題處理具體的錯誤。// 2.如果是異常體系,不管是ConnnectSql還是ServerStart及調用函數出錯,都不用檢查,因
//為拋出的異常異常會直接跳到main函數中catch捕獲的地方,main函數直接處理錯誤。int ConnnectSql()
{// 用戶名密碼錯誤if (...)return 1;// 權限不足if (...)return 2;
}int ServerStart() 
{if (int ret = ConnnectSql() < 0)return ret;int fd = socket()if(fd < 0)return errno;
}int main()
{if(ServerStart()<0)//...return 0;
}

?3. 很多的第三方庫都包含異常, 比如boost, gtest, gmock等等常用的庫, 那么我們使用它們也需要使用異常。
4. 部分函數使用異常更好處理, 比如構造函數沒有返回值, 不方便使用錯誤碼方式處理. 比如T& operator這樣的函數, 如果pos越界了只能使用異常或者終止程序處理, 沒辦法通過返回值表示錯誤.

C++異常的缺點:

1. 異常會導致程序的執行流亂跳, 并且非常的混亂, 并且是運行時出錯拋異常就會亂跳. 這會
導致我們跟蹤調試時以及分析程序時, 比較困難.


2. 異常會有一些性能的開銷. 當然在現代硬件速度很快的情況下, 這個影響基本忽略不計.


3. C++沒有垃圾回收機制,?資源需要自己管理. 有了異常非常容易導致內存泄漏、死鎖等異常安全問題. 這個需要使用RAII來處理資源的管理問題.


4. C++標準庫的異常體系定義得不好, 導致大家各自定義各自的異常體系, 非常的混亂.


5. 異常盡量規范使用, 否則后果不堪設想, 隨意拋異常, 外層捕獲的用戶苦不堪言, 所以異常規范有兩點:

一、拋出異常類型都繼承自一個基類.

二、函數是否拋異常, 拋什么異常, 都使用 func()?throw(異常類型)/noexception; 的方式規范化.

總結: 異常總體而言, 利大于弊, 所以工程中我們還是鼓勵使用異常的, 另外OO的語言基本都是
用異常處理錯誤, 這也可以看出這是大勢所趨.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/211409.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/211409.shtml
英文地址,請注明出處:http://en.pswp.cn/news/211409.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

翻轉二叉樹(圖解、前序遍歷、遞歸與非遞歸)

LCR 144. 翻轉二叉樹 - 力扣&#xff08;LeetCode&#xff09; 給定一棵二叉樹的根節點 root&#xff0c;請左右翻轉這棵二叉樹&#xff0c;并返回其根節點。 示例 1&#xff1a; 輸入&#xff1a;root [5,7,9,8,3,2,4] 輸出&#xff1a;[5,9,7,4,2,3,8] 提示&#xff1a; …

【11】Qt Designer

目錄 VSCode添加外部工具 QtDesigner PyUIC PyRCC 加載UI文件模板代碼 QMainWindow QWidget 常用知識點 1. 修改標題圖標 2. 圖片資源管理 3. 圖片按鈕 4. 加載對話框 5. 動態加載Widget 6. 修改主題 其他注意事項 事件被多次觸發 PyQt5提供了一個可視化圖形工…

【小沐學Python】Python實現WebUI網頁圖表(gradio)

文章目錄 1、簡介2、安裝3、基本測試3.1 入門代碼3.2 組件屬性3.3 多個輸入和輸出組件3.4 圖像示例3.5 聊天機器人3.6 模塊&#xff1a;更靈活、更可控3.7 進度條 結語 1、簡介 https://www.gradio.app/ Gradio是用友好的網絡界面演示機器學習模型的最快方法&#xff0c;因此…

【T+】暢捷通T+軟件安裝過程中停留在:正在配置產品位置或進度80%位置。

【問題描述】 暢捷通T軟件在安裝過程中&#xff0c; 進度條一直停留在【正在配置產品…】位置。 【解決方法】 打開【任務管理器】&#xff0c;想必這個如何打開&#xff0c;大家應該都會。 在【進程】中找到【DBConfig.exe】或者【Ufida.T.Tool.SM.DBConfig.exe】進程并結束…

TS條件類型、斷言及名義類型

文章將討論處理類型的幾個高級模式&#xff0c;包括模擬名義類型的類型烙印、利用條件類型的分配性質在類型層面操作類型&#xff0c;以及安全地擴展原型。 1 函數類型 TS在推導元組的類型時會放寬要求&#xff0c;推導出的結果盡量寬泛&#xff0c;不在乎元組的長度和各位置…

Spring Cloud Gateway使用和配置

Spring Cloud Gateway是Spring官方基于Spring 5.0&#xff0c;Spring Boot 2.0和Project Reactor等技術開發的網關&#xff0c;Spring Cloud Gateway旨在為微服務架構提供一種簡單而有效的統一的API路由管理方式。Spring Cloud Gateway作為Spring Cloud生態系中的網關&#xff…

Linux 基礎IO

文章目錄 前言基礎IO定義系統IO接口文件描述符重定向原理緩沖區刷新 前言 要知道每個函數/接口的全部參數和返回值建議去官網或者直接在Linux的man手冊中查&#xff0c;這不是復制粘貼函數用法的文章。 C語言文件讀寫介紹鏈接 基礎IO定義 IO是Input/Output的縮寫&#xff0c…

optional

參考資料&#xff1a; Java8 Optional用法和最佳實踐 - 掘金 一、背景 根據Oracle文檔&#xff0c;Optional是一個容器對象&#xff0c;可以包含也可以不包含非null值。Optional在Java 8中引入&#xff0c;目的是解決 NullPointerExceptions的問題。本質上&#xff0c;Optio…

2024年網絡安全競賽-網站滲透

網站滲透 (一)拓撲圖 1.使用滲透機對服務器信息收集,并將服務器中網站服務端口號作為flag提交; 使用nmap工具對靶機進行信息收集 2.使用滲透機對服務器信息收集,將網站的名稱作為flag提交; 訪問頁面即可 3.使用滲透機對服務器滲透,將可滲透頁面的名稱作為flag提交…

Python:核心知識點整理大全5-筆記

目錄 2. 使用方法pop()刪除元素 3. 彈出列表中任何位置處的元素 4. 根據值刪除元素 3 章 列表簡介 3.3 組織列表 3.3.1 使用方法 sort()對列表進行永久性排序 3.3.2 使用函數 sorted()對列表進行臨時排序 3.3.3 倒著打印列表 3.3.4 確定列表的長度 3.5 小結 2. 使用方…

軟件測試:測試用例八大要素模板

一、通用測試用例八要素 1、用例編號&#xff1b; 2、測試項目&#xff1b; 3、測試標題&#xff1b; 4、重要級別&#xff1b; 5、預置條件&#xff1b; 6、測試輸入&#xff1b; 7、操作步驟&#xff1b; 8、預期輸出 二、具體分析通用測試用例八要素 1、用例編號 一般是數字…

[NAND Flash 2.1] NAND Flash 閃存改變了現代生活

依公知及經驗整理&#xff0c;原創保護&#xff0c;禁止轉載。 專欄 《深入理解NAND Flash》 <<<< 返回總目錄 <<<< ? 1989年NAND閃存面世了&#xff0c;它曾經且正在改變了我們的日常生活。 NAND 閃存發明之所以偉大&#xff0c;是因為&#xff0c…

一個CV算法工程師在技術方面的小反思

極市導讀 正如作者所說,做一個算法工程師最重要的素質是在海量的算法方案中理解,吃透那些真正的干貨,然后不斷在實踐中去驗證,并總結吸收到自己的腦子里。本文記錄了作者在算法工程師這個崗位上一年后總結的一些關于技術上的經驗總結。>>加入極市CV技術交流群,走在計…

怎樣解決編譯后的exe文件運行時產生的錯誤?

編譯后的exe文件運行時&#xff0c;錯誤如下錯誤提示&#xff1a;Traceback (most recent call last):File "pd.py", line 1, in <module>from pdf2docx import parse ModuleNotFoundError: No module named pdf2docx 怎樣解決&#xff1f; 這個錯誤提示表明…

java數據結構面試題

1.棧和隊列的共同特點是&#xff08;只允許在端點處插入和刪除元素&#xff09; 4.棧通常采用的兩種存儲結構是&#xff08;線性存儲結構和鏈表存儲結構&#xff09; 5.下列關于棧的敘述正確的是&#xff08;D&#xff09; A.棧是非線性結構 B.棧是一種樹狀結構 C.棧具有先進先…

蘋果OS X系統介紹(Mac OS --> Mac OS X --> OS X --> macOS)

文章目錄 OS X系統介紹歷史與版本架構內核與低級系統圖形&#xff0c;媒體和用戶界面應用程序和服務 特性用戶友好強大的命令行安全性集成與互操作性 總結 OS X系統介紹 OS X是由蘋果公司為Macintosh計算機系列設計的基于UNIX的操作系統。其界面友好&#xff0c;易于使用&…

使用 nohup java - jar 不輸出日志

要在使用nohup java -jar命令時不輸出日志&#xff0c;可以將標準輸出和標準錯誤輸出重定向到特殊設備文件/dev/null。這樣做將會丟棄所有的輸出。 以下是在Linux中使用nohup java -jar命令并禁止輸出日志的示例&#xff1a; 復制代碼 nohup java -jar your-application.jar …

Python可視化(二)——Seaborn

Seaborn是一個基于matplotlib的可視化庫&#xff0c;其為用戶提供了高級接口&#xff0c;并且該工具還深度集成了pandas的數據結構。并且該工具該集成了很多數據庫&#xff0c;配合官網給出的代碼示例&#xff0c;可以更方便的進行操作。 官網對它的介紹為&#xff1a; Seabo…

Servlet學習筆記

簡介 瀏覽器請求處理流程&#xff1a;瀏覽器發請求 > 服務器tomcat( > 應用程序 ( > servlet) ) Servlet應用的三大作用域&#xff1a;request&#xff0c;session&#xff0c;application tomcat存放項目的層級結構 注釋&#xff1a;servlet原引用包名 javax.serv…

卡爾曼濾波器

歡迎訪問我的博客首頁。 卡爾曼濾波器 1. 參考 1. 參考 卡爾曼濾波器&#xff0c;B 站&#xff0c;2020。擴展卡爾曼濾波器&#xff0c;CSDN&#xff0c;2023。