c++20引入的三路比較操作符<=>

目錄

一、簡介

二、三向比較的返回類型

2.1 std::strong_ordering

2.2 std::weak_ordering

2.3 std::partial_ordering

三、對基礎類型的支持

四、自動生成的比較運算符函數

4.1 std::rel_ops的作用

4.2 使用<=>

五、兼容他舊代碼


一、簡介

c++20引入了三路比較操作符(或者三向比較)<=>(three-way comparison operator),也叫太空船(spaceship)操作符。

<=>也是一個二元關系運算符,但它不像其他二元比較操作符那樣返回類型是布爾類型,而是根據用戶指明的三種類型之一:partial_ordering、weak_ordering和strong_ordering,定義于標準庫頭文件<compare>中,默認為strong_ordering類型。

  • 偏序partial_ordering表達了比較關系中的偏序關系,即給定類的任意兩個對象不一定可比較。例如給定一棵對象樹,假設父節點比子節點大,<=>得到的結果將為greater,但不是任意兩個節點都可比較,此時它們的關系為unordered。對于偏序關系的排序,使用拓撲排序算法將獲得正確的結果。
  • 弱序weak_ordering表達了比較關系中的全序關系,即給定類的任意兩個對象都能比較,將既不大于也不小于的關系定義為等價(equivalent)關系。假設長方形類按照面積比較就是弱序關系,長寬分別為2和6的矩形與長寬分別為3和4的比較,面積都為12(既不大于也不小于)?,那么它們是等價的,但不相等是因為可以通過長寬區分出來它們不一樣。標準庫中的std::sort要求關系至少為弱序的才能正確工作。
  • 強序strong_ordering與弱序一樣,當對等價關系進行了約束即為相等(equal)關系。考慮正方形類按照面積比較就是強序關系,因為面積一樣的正方形無法像長方形那樣通過外表能區分出來,即它們是相等的。一些查找算法要求關系為強序才能正確工作。

此外<=>的結果也與字符串比較函數strcmp類似,能夠通過正負判斷關系:當結果大于0表示大于關系,等于0表示等價、等于關系,小于0表示小于關系。

顧名思義,三向比較就是在形如lhs <=> rhs的表達式中,兩個比較的操作數lhs和rhs通過<=>比較可能產生3種結果,該結果可以和0比較,小于0、等于0或者大于0分別對應lhs < rhs、lhs == rhs和lhs > rhs。舉例來說:

#include<iostream>int main(int argc,char* argv[]){bool b = 7<=>11 <0;std::cout<<std::boolalpha<<b<<std::endl;b = 7<=>11 ==0;std::cout<<std::boolalpha<<b<<std::endl;b = 7<=>11 >0;std::cout<<std::boolalpha<<b<<std::endl;b = 7<=>7 ==0;std::cout<<std::boolalpha<<b<<std::endl;return 0;
}

輸出:

true
false
false
true

請注意,運算符<=>的返回值只能與0和自身類型來比較,如果同其他數值比較,編譯器會報錯:

#include<iostream>int main(int argc,char* argv[]){bool b = 7<=>11 <100;//編譯失敗,<=>的結果不能與除0以外的數值比較std::cout<<std::boolalpha<<b<<std::endl;return 0;
}


二、三向比較的返回類型

<=>的返回結果并不是一個普通類型,根據標準三向比較會返回3種類型,分別為std::strong_ordering、std::weak_ordering以及std::partial_ordering,而這3種類型又會分為有3~4種最終結果。


2.1 std::strong_ordering

std::strong_ordering類型有3種比較結果,分別為std::strong_ ordering::less、std::strong_ordering::equal以及std::strong_ ordering::greater。表達式lhs <=> rhs分別表示lhs < rhs、lhs == rhs以及lhs > rhs。std::strong_ordering類型的結果強調的是strong的含義,表達的是一種可替換性,簡單來說,若lhs == rhs,那么在任何情況下rhs和lhs都可以相互替換,也就是fx(lhs) == fx(rhs)。

對于基本類型中的int類型,三向比較返回的是std::strong_ordering,例如:

用MSVC編譯運行以上代碼,會在輸出窗口顯示class std::strong_ ordering,刻意使用MSVC是因為它的typeid(x).name()可以輸出友好可讀的類型名稱。

對于有復雜結構的類型,std::strong_ordering要求其數據成員和基類的三向比較結果都為std::strong_ordering。例如:

#include<iostream>struct B{int a;long b;auto operator <=> (const B&) const = default;
};struct D : B{short c;auto operator <=> (const D&) const = default;
};int main(int argc,char* argv[]){D x1,x2;std::cout<<typeid(decltype(x1 <=> x2)).name()<<std::endl;
}

上面這段代碼用MSVC編譯運行會輸出class std::strong_ordering。

請注意,默認情況下自定義類型是不存在三向比較運算符函數的,需要用戶顯式默認聲明,比如在結構體B和D中聲明auto operator <=> (const B&) const = default;和auto operator <=> (const D&)const = default;。

?如果刪除基類的<=>運算符,派生類顯式定義的<=>將被刪除。

如果刪除派生類的<=>,保留基類的<=>,還可以運行。

?對結構體B而言,由于int和long的比較結果都是std::strong_ordering,因此結構體B的三向比較結果也是std::strong_ordering。同理,對于結構體D,其基類和成員的比較結果是std::strong_ordering,D的三向比較結果同樣是std::strong_ordering。

另外,明確運算符的返回類型,使用std::strong_ ordering替換auto也是沒問題的。


2.2 std::weak_ordering

std::weak_ordering類型也有3種比較結果,分別為std::weak_ ordering::less、std::weak_ordering::equivalent以及std::weak_ ordering::greater。std::weak_ordering的含義正好與std::strong_ ordering相對,表達的是不可替換性。即若有lhs == rhs,則rhs和lhs不可以相互替換,也就是fx(lhs) != fx(rhs)。這種情況在基礎類型中并沒有,但是它常常發生在用戶自定義類中,比如一個大小寫不敏感的字符串類:

#include <compare>
#include <string>
#include <iostream>int ci_compare(const char* s1, const char* s2)
{while (tolower(*s1) == tolower(*s2++)) {if (*s1++ == '\0') {return 0;}}return tolower(*s1) - tolower(*--s2);
}class CIString {
public:CIString(const char *s) : str_(s) {}std::weak_ordering operator<=>(const CIString& b) const {return ci_compare(str_.c_str(), b.str_.c_str()) <=> 0;//strong_ordering返回為weak_ordering類型,實際上發生了類型轉換}
private:std::string str_;
};int main(int argc,char* argv[])
{auto res = 'a'<=>'a';std::cout << typeid(res).name()<<std::endl;      //strong_orderingstd::cout << typeid(res<=>0).name()<<std::endl;  //strong_orderingstd::cout << typeid( ((std::weak_ordering)res) ).name()<<std::endl; //strong_ordering可以轉為weak_orderingCIString s1{ "HELLO" }, s2{"hello"};std::cout << std::boolalpha << (s1 <=> s2 == 0)<<std::endl; // 輸出為truestd::cout << typeid(s1<=>s2).name()<<std::endl;  //weak_orderingreturn 0;
}

?

以上代碼實現了一個簡單的大小寫不敏感的字符串類,它對于s1和s2的比較結果是std::weak_ordering::equivalent,表示兩個操作數是等價的,但是它們不是相等的也不能相互替換。當std::weak_ordering和std::strong_ ordering同時出現在基類和數據成員的類型中時,該類型的三向比較結果是std::weak_ordering,例如:

#include <compare>
#include <string>
#include <iostream>int ci_compare(const char* s1, const char* s2)
{while (tolower(*s1) == tolower(*s2++)) {if (*s1++ == '\0') {return 0;}}return tolower(*s1) - tolower(*--s2);
}class CIString {
public:CIString(const char *s) : str_(s) {}std::weak_ordering operator<=>(const CIString& b) const {return ci_compare(str_.c_str(), b.str_.c_str()) <=> 0;//strong_ordering返回為weak_ordering類型,實際上發生了類型轉換}
private:std::string str_;
};struct B{int a=0;long b=0;std::strong_ordering operator <=> (const B&) const = default;
};struct D : B{CIString c{""};auto operator <=> (const D&) const = default;
};int main(int argc,char* argv[])
{D w1,w2;std::cout << std::boolalpha << (w1 <=> w2 == 0)<<std::endl; // 輸出為truestd::cout << std::boolalpha << (w1 <=> w2 == std::weak_ordering::equivalent)<<std::endl; // 輸出為truestd::cout << typeid(w1<=>w2).name()<<std::endl;  //weak_orderingreturn 0;
}

用MSVC編譯運行上面這段代碼會輸出class std::weak_ordering,因為D中的數據成員CIString的三向比較結果為std::weak_ordering。請注意,如果顯式聲明默認三向比較運算符函數為std::strong_ordering operator <=> (const D&) const = default;,那么一定會遭遇到一個編譯錯誤。


2.3 std::partial_ordering

std::partial_ordering類型有4種比較結果,分別為std::partial_ ordering::less、std::partial_ordering::equivalent、std::partial_ ordering::greater以及std::partial_ordering::unordered。std:: partial_ordering約束力比std::weak_ordering更弱,它可以接受當lhs == rhs時rhs和lhs不能相互替換,同時它還能給出第四個結果std::partial_ordering::unordered,表示進行比較的兩個操作數沒有關系。比如基礎類型中的浮點數:

#include <iostream>int main(int argc,char* argv[])
{std::cout << typeid(decltype(7.7 <=> 11.1)).name();//輸出partial_orderingreturn 0;
}

用MSVC編譯運行以上代碼會輸出class std::partial_ordering。之所以會輸出class std::partial_ordering而不是std::strong_ordering,是因為浮點的集合中存在一個特殊的NaN,它和其他浮點數值是沒關系的:

#include <iostream>int main(int argc,char* argv[])
{std::cout<<std::boolalpha<< ((0.0/0.0 <=> 1.0) == std::partial_ordering::unordered);//輸出truereturn 0;
}

這段代碼編譯輸出的結果為true。

當std::weak_ordering和std:: partial_ordering同時出現在基類和數據成員的類型中時,該類型的三向比較結果是std::partial_ordering,例如:

#include <compare>
#include <string>
#include <iostream>int ci_compare(const char* s1, const char* s2)
{while (tolower(*s1) == tolower(*s2++)) {if (*s1++ == '\0') {return 0;}}return tolower(*s1) - tolower(*--s2);
}class CIString {
public:CIString(const char *s) : str_(s) {}std::weak_ordering operator<=>(const CIString& b) const {return ci_compare(str_.c_str(), b.str_.c_str()) <=> 0;//strong_ordering返回為weak_ordering類型,實際上發生了類型轉換}
private:std::string str_;
};struct B{int a=0;long b=0;std::strong_ordering operator <=> (const B&) const = default;
};struct D : B{CIString c{""};float u=0.0;auto operator <=> (const D&) const = default;
};int main(int argc,char* argv[])
{D w1,w2;std::cout << std::boolalpha << (w1 <=> w2 == 0)<<std::endl; // 輸出為truestd::cout << std::boolalpha << (w1 <=> w2 == std::partial_ordering::equivalent)<<std::endl; // 輸出為truestd::cout << typeid(w1<=>w2).name()<<std::endl;  //partial_orderingreturn 0;
}


用MSVC編譯運行以上代碼會輸出class std::partial_ordering,因為D中的數據成員u的三向比較結果為std::partial_ordering,同樣,顯式聲明為其他返回類型也會讓編譯器報錯。在C++20的標準庫中有一個模板元函數std::common_comparison_category,它可以幫助我們在一個類型合集中判斷出最終三向比較的結果類型,當類型合集中存在不支持三向比較的類型時,該模板元函數返回void。

再次強調一下,std::strong_ordering、std::weak_ordering和`std::partial_ordering`只能與`0`和類型自身比較。深究其原因,是這3個類只實現了參數類型為自身類型和`nullptr_t的比較運算符函數。


三、對基礎類型的支持

  • 3.1.對兩個算術類型的操作數進行一般算術轉換,然后進行比較。其中整型的比較結果為std::strong_ordering,浮點型的比較結果為std::partial_ordering。例如7 <=> 11.1中,整型7會轉換為浮點類型,然后再進行比較,最終結果為std::partial_ordering類型。
  • 3.2.對于無作用域枚舉類型和整型操作數,枚舉類型會轉換為整型再進行比較,無作用域枚舉類型無法與浮點類型比較:
enum color {red
};auto r = red <=> 11;   //編譯成功
auto r = red <=> 11.1; //編譯失敗
  • 3.3.對兩個相同枚舉類型的操作數比較結果,如果枚舉類型不同,則無法編譯。
  • 3.4.對于其中一個操作數為bool類型的情況,另一個操作數必須也是bool類型,否則無法編譯。比較結果為std::strong_ordering。
  • 3.5.不支持作比較的兩個操作數為數組的情況,會導致編譯出錯,例如:
int arr1[5];
int arr2[5];
auto r = arr1 <=> arr2; // 編譯失敗
  • 3.6.對于其中一個操作數為指針類型的情況,需要另一個操作數是同樣類型的指針,或者是可以轉換為相同類型的指針,比如數組到指針的轉換、派生類指針到基類指針的轉換等,最終比較結果為std::strong_ordering:
char arr1[5];
char arr2[5];
char* ptr = arr2;
auto r = ptr <=> arr1;

上面的代碼可以編譯成功,若將代碼中的arr1改寫為int arr1[5],則無法編譯,因為int [5]無法轉換為char *。如果將char * ptr = arr2;修改為void * ptr = arr2;,代碼就可以編譯成功了。


四、自動生成的比較運算符函數

4.1 std::rel_ops的作用

標準庫中提供了一個名為std::rel_ops的命名空間,在用戶自定義類型已經提供了==運算符函數和<運算符函數的情況下,幫助用戶實現其他4種運算符函數,包括!=、>、<=和>=。

?代碼:

#include <compare>
#include <string>
#include <iostream>
#include <utility>int ci_compare(const char* s1, const char* s2)
{while (tolower(*s1) == tolower(*s2++)) {if (*s1++ == '\0') {return 0;}}return tolower(*s1) - tolower(*--s2);
}class CIString2 {
public:CIString2(const char* s) : str_(s) {}bool operator < (const CIString2& b) const {return ci_compare(str_.c_str(), b.str_.c_str()) < 0;}bool operator== (const CIString2& b) const {return ci_compare(str_.c_str(), b.str_.c_str()) == 0;}
private:std::string str_;
};int main(int argc,char* argv[])
{using namespace std::rel_ops;CIString2 s1( "hello" ), s2( "world" );bool r = true;r = s1 == s2;std::cout<<std::boolalpha<<r<<std::endl;r = s1 != s2;std::cout<<std::boolalpha<<r<<std::endl;r = s1 > s2;std::cout<<std::boolalpha<<r<<std::endl;r = s1 >= s2;std::cout<<std::boolalpha<<r<<std::endl;r = s1 < s2;std::cout<<std::boolalpha<<r<<std::endl;r = s1 <= s2;std::cout<<std::boolalpha<<r<<std::endl;return 0;
}

輸出:

false
true
false
false
true
true

4.2 使用<=>

不過因為C++20標準有了三向比較運算符的關系,所以不推薦上面這種做法了。C++20標準規定,如果用戶為自定義類型聲明了三向比較運算符,那么編譯器會為其自動生成<、>、<=和>=這4種運算符函數。對于CIString我們可以直接使用這4種運算符函數:

#include <compare>
#include <string>
#include <iostream>
#include <utility>int ci_compare(const char* s1, const char* s2)
{while (tolower(*s1) == tolower(*s2++)) {if (*s1++ == '\0') {return 0;}}return tolower(*s1) - tolower(*--s2);
}class CIString {
public:CIString(const char *s) : str_(s) {}//   bool operator== (const CIString& b) const {
//       return ci_compare(str_.c_str(), b.str_.c_str()) == 0;
//   }std::weak_ordering operator<=>(const CIString& b) const {return ci_compare(str_.c_str(), b.str_.c_str()) <=> 0;//strong_ordering返回為weak_ordering類型,實際上發生了類型轉換}
private:std::string str_;
};int main(int argc,char* argv[])
{CIString s1( "hello" ), s2( "world" );bool r = true;// r = s1 == s2;// std::cout<<std::boolalpha<<r<<std::endl;// r = s1 != s2;// std::cout<<std::boolalpha<<r<<std::endl;r = s1 > s2;std::cout<<std::boolalpha<<r<<std::endl;r = s1 >= s2;std::cout<<std::boolalpha<<r<<std::endl;r = s1 < s2;std::cout<<std::boolalpha<<r<<std::endl;r = s1 <= s2;std::cout<<std::boolalpha<<r<<std::endl;return 0;
}

輸出

false
false
true
true

那么這里就會產生一個疑問,很明顯三向比較運算符能表達兩個操作數是相等或者等價的含義,為什么標準只允許自動生成4種運算符函數,卻不能自動生成==和=!這兩個運算符函數呢?實際上這里存在一個嚴重的性能問題。在C++20標準擬定三向比較的早期,是允許通過三向比較自動生成6個比較運算符函數的,而三向比較的結果類型也不是3種而是5種,多出來的兩種分別是std::strong_ equality和std::weak_equality。但是在提案文檔p1190中提出了一個嚴重的性能問題。簡單來說,假設有一個結構體:

struct S {std::vector<std::string> names;auto operator<=>(const S &) const = default;
};

它的三向比較運算符的默認實現這樣的:

template<typename T>
std::strong_ordering operator<=>(const std::vector<T>& lhs, const std::vector<T> & rhs) 
{size_t min_size = min(lhs.size(), rhs.size());for (size_t i = 0; i != min_size; ++i) {if (auto const cmp = std::compare_3way(lhs[i], rhs[i]); cmp != 0) {return cmp;}}return lhs.size() <=> rhs.size();
}


這個實現對于<和>這樣的運算符函數沒有問題,因為需要比較容器中的每個元素。但是==運算符就顯得十分低效,對于==運算符高效的做法是先比較容器中的元素數量是否相等,如果元素數量不同,則直接返回false:

template<typename T>
bool operator==(const std::vector<T>& lhs, const std::vector<T>& rhs)
{const size_t size = lhs.size();if (size != rhs.size()) {return false;}for (size_t i = 0; i != size; ++i) {if (lhs[i] != rhs[i]) {return false;}}return true;
}

想象一下,如果標準允許用三向比較的算法自動生成==運算符函數會發生什么事情,很多舊代碼升級編譯環境后會發現運行效率下降了,尤其是在容器中元素數量眾多且每個元素數據量龐大的情況下。很少有程序員會注意到三向比較算法的細節,導致這個性能問題難以排查。基于這種考慮,C++委員會修改了原來的三向比較提案,規定聲明三向比較運算符函數只能夠自動生成4種比較運算符函數。由于不需要負責判斷是否相等,因此std::strong_equality和std::weak_ equality也退出了歷史舞臺。對于==和!=兩種比較運算符函數,只需要多聲明一個==運算符函數,!=運算符函數會根據前者自動生成:

#include <compare>
#include <string>
#include <iostream>
#include <utility>int ci_compare(const char* s1, const char* s2)
{while (tolower(*s1) == tolower(*s2++)) {if (*s1++ == '\0') {return 0;}}return tolower(*s1) - tolower(*--s2);
}class CIString {
public:CIString(const char *s) : str_(s) {}bool operator== (const CIString& b) const {return ci_compare(str_.c_str(), b.str_.c_str()) == 0;}std::weak_ordering operator<=>(const CIString& b) const {return ci_compare(str_.c_str(), b.str_.c_str()) <=> 0;//strong_ordering返回為weak_ordering類型,實際上發生了類型轉換}
private:std::string str_;
};int main(int argc,char* argv[])
{CIString s1( "hello" ), s2( "world" );bool r = true;r = s1 == s2;std::cout<<std::boolalpha<<r<<std::endl;r = s1 != s2;std::cout<<std::boolalpha<<r<<std::endl;r = s1 > s2;std::cout<<std::boolalpha<<r<<std::endl;r = s1 >= s2;std::cout<<std::boolalpha<<r<<std::endl;r = s1 < s2;std::cout<<std::boolalpha<<r<<std::endl;r = s1 <= s2;std::cout<<std::boolalpha<<r<<std::endl;return 0;
}
false
true
false
false
true
true

五、兼容他舊代碼

現在C++20標準已經推薦使用<=>和==運算符自動生成其他比較運算符函數,而使用<、==以及std::rel_ops生成其他比較運算符函數則會因為std::rel_ops已經不被推薦使用而被編譯器警告。那么對于老代碼,我們是否需要去實現一套<=>和==運算符函數呢?其實大可不必,C++委員會在裁決這項修改的時候已經考慮到老代碼的維護成本,所以做了兼容性處理,即在用戶自定義類型中,實現了<、==運算符函數的數據成員類型,在該類型的三向比較中將自動生成合適的比較代碼。比如:

#include <iostream>struct Legacy {int n=0;bool operator==(const Legacy& rhs) const{return n == rhs.n;}bool operator<(const Legacy& rhs) const{return n < rhs.n;}
};struct TreeWay {Legacy m;std::strong_ordering operator<=>(const TreeWay &) const = default;
};int main(int argc,char* argv[])
{TreeWay t1, t2;bool r = t1 < t2;std::cout<<std::boolalpha<<r<<std::endl;return 0;
}

在上面的代碼中,結構體TreeWay的三向比較操作會調用結構體Legacy中的<和==運算符來完成,其代碼類似于:

struct TreeWay {Legacy m;std::strong_ordering operator<=>(const TreeWay& rhs) const {if (m < rhs.m) return std::strong_ordering::less;if (m == rhs.m) return std::strong_ordering::equal;return std::strong_ordering::greater;}
};

需要注意的是,這里operator<=>必須顯式聲明返回類型為std::strong_ ordering,使用auto是無法通過編譯的。

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

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

相關文章

計算機網絡相關面試題

一、HTTP1.1和HTTP2的區別 HTTP/1&#xff08;主要指 HTTP/1.1&#xff09;和 HTTP/2 是 Web 協議發展中的兩個重要版本&#xff0c;二者在性能、協議機制和功能特性上有顯著差異。以下從多個維度對比分析&#xff0c;并結合具體案例說明&#xff1a; 一、連接與請求處理方式 1…

圖論算法精解(Java 實現):從基礎到高頻面試題

一、圖的基礎表示方法 1.1 鄰接矩陣&#xff08;Adjacency Matrix&#xff09; 鄰接矩陣是表示圖的一種直觀方式&#xff0c;它使用一個二維數組來存儲節點之間的連接關系。對于一個有 n 個節點的圖&#xff0c;鄰接矩陣是一個 nn 的矩陣&#xff0c;其中 matrix [i][j] 表示…

江科大TIM定時器hal庫實現

定時器相關hal庫函數 hal庫的定時器函數相比于標準庫&#xff0c;多了很多的中斷回調函數&#xff0c;同時對于定時器的初始化也改成使用句柄一次性順帶連帶DMA等功能一起初始化了 typedef struct {uint32_t Prescaler; /*定時器的預分頻值*/uint32_t CounterMode; …

CentOS 10:啟動telnet服務

參考&#xff0c; 鳥哥私房菜 - 第七章、網路安全與主機基本防護&#xff1a;限制埠口, 網路升級與 SELinux 7.3.3 埠口與服務的啟動/關閉及開機時狀態設定 我們知道系統的 Telnet 服務通常是以 super daemon 來控管的&#xff0c;請您啟動您系統的 telnet 試看看。 1 要啟動 …

Taro 安全區域

目錄 一、問題描述 二、問題解決 1、頂部劉海區 2、底部小黑條 一、問題描述 安全區域主要是為了避免劉海屏或底部欄遮擋&#xff0c;而造成的不良顯示效果。 本次將針對以下兩點進行考量&#xff1a; 1、頂部劉海屏區 2、蘋果X底部小黑條 二、問題解決 通過Taro.getS…

【Java微服務組件】分布式協調P1-數據共享中心簡單設計與實現

歡迎來到啾啾的博客&#x1f431;。 記錄學習點滴。分享工作思考和實用技巧&#xff0c;偶爾也分享一些雜談&#x1f4ac;。 歡迎評論交流&#xff0c;感謝您的閱讀&#x1f604;。 目錄 引言設計一個共享數據中心選擇數據模型鍵值對設計 數據可靠性設計持久化快照 &#xff08…

在SpringBoot項目中,使用單元測試@Test

1.引入依賴 <!--單元測試Test的依賴--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>3.2.1</version> </dependency> 2.在src/test/java目錄…

在Java中,將Object對象轉換為具體實體類對象

在Java中&#xff0c;將Object對象轉換為具體實體類對象可以通過以下幾種方法實現&#xff1a; 1?.使用instanceof關鍵字進行類型檢查和轉換?&#xff1a; 首先&#xff0c;使用instanceof關鍵字檢查Object對象是否為目標實體類的類型。 如果是&#xff0c;則進行強制類型…

JAVA學習-練習試用Java實現“音頻文件的讀取與寫入 :使用Java音頻庫處理音頻數據”

問題&#xff1a; java語言編輯&#xff0c;實現音頻文件的讀取與寫入 &#xff1a;使用Java音頻庫處理音頻數據。 解答思路&#xff1a; 在Java中處理音頻文件通常需要使用第三方庫&#xff0c;例如javax.sound.sampled包&#xff0c;它提供了處理音頻文件的基本功能。以下是一…

Flink架構概覽,Flink DataStream API 的使用,FlinkCDC的使用

一、Flink與其他組件的協同 Flink 是一個分布式、高性能、始終可用、準確一次&#xff08;Exactly-Once&#xff09;語義的流處理引擎&#xff0c;廣泛應用于大數據實時處理場景中。它與 Hadoop 生態系統中的組件可以深度集成&#xff0c;形成完整的大數據處理鏈路。下面我們從…

linux 查看java的安裝路徑

一、驗證Java安裝狀態 java -version正常安裝會顯示版本信息&#xff1a; openjdk version "1.8.0_65" OpenJDK Runtime Environment (build 1.8.0_65-b17) OpenJDK 64-Bit Server VM (build 25.65-b01, mixed mode)二、檢查環境變量配置 若已配置JAVA_HOME&#…

2025-5-21 個人筆記篇matlab小筆記和clang基礎使用(簡單記錄)

個人筆記篇 再不記錄就找不到了&#xff0c;之前學的一點基礎&#xff0c;看看就行,請不要提問,因為很久了>_<(至少我看來是這樣的) matlab小筆記 % 開繪制(新建) figure % 設置繪制標題 title(標題); % 設置繪制的X軸Lable xlabel(x); % 設置繪制的y軸Lable ylabel(cos…

前端JavaScript-嵌套事件

點擊 如果在多層嵌套中&#xff0c;對每層都設置事件監視器&#xff0c;試試看 <!DOCTYPE html> <html lang"cn"> <body><div id"container"><button>點我&#xff01;</button></div><pre id"output…

網感驅動下開源AI大模型AI智能名片S2B2C商城小程序源碼的實踐路徑研究

摘要&#xff1a;在數字化浪潮中&#xff0c;網感已成為內容創作者與商業運營者必備的核心能力。本文以開源AI大模型、AI智能名片及S2B2C商城小程序源碼為技術載體&#xff0c;通過解析網感培養與用戶需求洞察的內在關聯&#xff0c;提出"數據驅動-場景適配-價值重構"…

AG-UI:重構AI代理與前端交互的下一代協議標準

目錄 技術演進背景與核心價值協議架構與技術原理深度解析核心功能與標準化事件體系典型應用場景與實戰案例開發者生態與集成指南行業影響與未來展望1. 技術演進背景與核心價值 1.1 AI交互的三大痛點 當前AI應用生態面臨三大核心挑戰: 交互碎片化:LangGraph、CrewAI等框架各…

游戲引擎學習第301天:使用精靈邊界進行排序

回顧并為今天的內容做準備 昨天&#xff0c;我們解決了一些關于排序的問題&#xff0c;這對我們清理長期存在的Z軸排序問題很有幫助。這個問題我們一直想在開始常規游戲代碼之前解決。雖然不確定是否完全解決了問題&#xff0c;但我們提出了一個看起來合理的排序標準。 有兩點…

Ajax快速入門教程

輸入java時&#xff0c;頁面并沒有刷新但是下面自動聯想出了跟java有關的東西&#xff0c;像這種就叫異步交互 它不會妨礙你的輸入&#xff0c;同時還能夠同步進行對于java相關聯想詞的推送 發送異步請求需要借助工具axios 引入axios&#xff0c;可以直接在scripts中引入 get和…

Anti Spy安卓版:智能防護,守護手機安全

Anti Spy安卓版是一款專為安卓設備設計的智能防護應用&#xff0c;旨在幫助用戶實時防護手機安全&#xff0c;抵御間諜軟件、惡意軟件和其他潛在威脅。它基于人工智能和啟發式搜索方法的引擎&#xff0c;能夠檢測并阻止已知和未知的間諜軟件、后門程序、賬單欺詐、短信欺詐、電…

超低延遲音視頻直播技術的未來發展與創新

引言 音視頻直播技術正在深刻改變著我們的生活和工作方式&#xff0c;尤其是在教育、醫療、安防、娛樂等行業。無論是全球性的體育賽事、遠程醫療、在線教育&#xff0c;還是智慧安防、智能家居等應用場景&#xff0c;都離不開音視頻技術的支持。為了應對越來越高的需求&#x…

系統架構設計(十二):統一過程模型(RUP)

簡介 RUP 是由 IBM Rational 公司提出的一種 面向對象的軟件工程過程模型&#xff0c;以 UML 為建模語言&#xff0c;是一種 以用例為驅動、以架構為中心、迭代式、增量開發的過程模型。 三大特征 特征說明以用例為驅動&#xff08;Use Case Driven&#xff09;需求分析和測…