【C++】右值引用 移動語義

目錄

    • 前言
    • 一、右值引用與移動語義
      • 1.1 左值引用和右值引用
      • 1.2 右值引用使用場景和意義
      • 1.3 右值引用引用左值及其一些更深入的使用場景分析
        • 1.3.1 完美轉發
    • 二、新的類功能
    • 三、可變參數模板


前言

本篇文章我們繼續來聊聊C++11新增的一些語法——右值引用,我們在之前就已經講過了左值引用,并且左值引用給我們帶來了很多的好處直接減少了拷貝操作提高了效率,那么右值引用到底起什么作用呢?下面我們一起來學習吧!!

一、右值引用與移動語義

1.1 左值引用和右值引用

有關引用我們在之前的文章就講過,它其實就是給別人取別名,所以無論是左值引用還是右值引用都是給別人取別名,只不過取別名的對象不一樣罷了,左值引用就是給左值取別名,右值引用就是給右值取別名!!

什么是左值?什么是左值引用?

左值就是一個表示數據的表達式(如變量名或者解引用的指針),說的再通俗一點它是一個變量,標識一塊空間,空間中存儲著數據。我們可以獲取它的地址+可以對它賦值,左值可以出現賦值符號的左邊,右值不能出現在賦值符號左邊。定義時const修飾符后的左值,不能給他賦值,但是可以取它的地址。左值引用就是給左值的引用,給左值取別名。

int main()
{// 以下的p、b、c、*p都是左值int* p = new int(0);int b = 1;const int c = 2;// 以下幾個是對上面左值的左值引用int*& rp = p;int& rb = b;const int& rc = c;int& pvalue = *p;return 0;
}

什么是右值?什么是右值引用?

右值也是一個表示數據的表達式,如:字面常量、表達式返回值,函數返回值(臨時對象不能左值返回)等等,說的再通俗一點右值就是一個常量,它沒有變量去標識,因此也就不能取到它的地址,它只是一個數據!!右值引用就是對右值的引用,給右值取別名。

int main()
{double x = 1.1, y = 2.2;// 以下幾個都是常見的右值10;x + y;fmin(x, y);// 以下幾個都是對右值的右值引用int&& rr1 = 10;double&& rr2 = x + y;double&& rr3 = fmin(x, y);// 下面的語句都會編譯報錯,左操作數必須為左值//10 = 1;//x + y = 1;//fmin(x, y) = 1;return 0;
}

需要注意的是:右值是不能取地址的,但是給右值取別名后,會導致右值被存儲到特定位置,且可以取到該位置的地址。也就是說,不能取字面量 10 的地址,但是 rr1 引用后,可以對 rr1 取地址,也可以修改 rr1。如果不想 rr1 被修改,可以用 const int&& rr1 去引用。注:rr1 和 rr2 都是左值。

在這里插入圖片描述

左值可以引用右值嗎?右值可以引用左值嗎?

int main()
{// 左值引用可以引用右值嗎? const的左值引用可以double x = 1.1, y = 2.2;//double& rr1 = x + y;	// 編譯報錯, x + y是右值, 也是臨時對象, 臨時對象具有常屬性, 需要const保證權限平行const double& rr2 = x + y; // 可以// 右值引用可以引用左值嗎?不可以, 可以引用move以后的左值int a = 10;//int&& rr3 = a; // 編譯報錯, 右值引用不能引用左值int&& rr4 = 10;  // 可以, 10是右值, 對右值取別名int&& rr5 = move(a);  // move(a)的本質是得到一個右值表達式return 0;
}

左值引用與右值引用總結:左值引用只能引用左值,不能引用右值。但是 const 左值引用既可引用左值,也可引用右值。右值引用只能引用右值,不能引用左值,但是右值引用可以 move 以后的左值。

其實上面并不是右值引用的使用場景,因為const左值引用既能做到對左值引用又能做到對右值引用,那么增加右值引用不就是多余的嗎!!下面我們繼續來看看右值引用的使用場景。

1.2 右值引用使用場景和意義

左值引用解決的問題:

做參數:a. 減少拷貝,提高效率。b. 做輸出型參數
做返回值:a. 減少拷貝,提高效率(不能返回臨時對象的引用)。

從上述中我們可以知道左值引用的短板就是不能返回臨時對象的引用,那么對于自定義類型對象返回時,是不是一定會進行一次深拷貝的操作,所以右值引用設計出來就是為了解決此類問題的。

在講右值引用前,我們先來了解一組概念:右值有兩類,第一類是純右值,即內置類型右值;第二類是將亡值,即自定義類型右值。右值將亡值的資源可以轉移到指定的對象。

在這里插入圖片描述

就好比日常生活中,有些重癥患者已經到了病入膏肓的情況,它可以將自身的一些器官捐贈給有需要的人身上。右值引用+移動語義最重要的是理解資源轉移的過程!

下面我們通過一段代碼來進行分析:

namespace bit
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷貝構造string(const string& s):_str(nullptr){cout << "string(const string& s) -- 深拷貝" << endl;string tmp(s._str);swap(tmp);}// 賦值重載string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷貝" << endl;string tmp(s);swap(tmp);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}string& operator+=(char ch){push_back(ch);return *this;}string operator+(char ch){string tmp(*this);tmp += ch;return tmp;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity; // 不包含最后做標識的\0};bit::string to_string(int value){bool flag = true;if (value < 0){flag = false;value = 0 - value;}bit::string str;while (value > 0){int x = value % 10;value /= 10;str += ('0' + x);}if (flag == false){str += '-';}std::reverse(str.begin(), str.end());return str; }
}int main()
{curry::string ret1 = curry::to_string(1234);return 0;
}

我們先來看看沒有C++11之前未新增移動拷貝的情況:

在這里插入圖片描述

從上圖我們也可以看到編譯器優化后只調用了一次深拷貝操作,下面當我們在自定義類型中添加了移動構造后,看看編譯器會做什么處理:

在這里插入圖片描述

從上圖我們可以看到當新增移動拷貝函數之后,編譯器識別為右值類型就會優先去調用移動拷貝函數,相較于深拷貝移動拷貝只是將資源進行了轉移——得到那塊空間的起始地址就能訪問那塊空間的所有資源!!避免了拷貝的操作,移動拷貝實際并不是真正的拷貝,其實就可以理解為改變了接收資源的對象!!所以右值引用在這種情況下提供了非常大的價值!!如果返回的對象是一個數組、一棵樹呢?是不是極大程度上減少了拷貝,提高了效率!!移動語義包括移動構造移動賦值兩個成員函數!!

注:右值引用并不是直接作為返回值起作用的,右值引用返回臨時對象跟左值引用返回對象的情況是一樣的,臨時對象都是會被銷毀的!!不能直接返回臨時對象的左右值引用!!


我們通過監視窗口來繼續看看移動構造的具象過程:

int main()
{curry::string s1("hello world");curry::string ret1 = s1;curry::string ret2 = (s1 + '1');curry::string ret3 = move(s1); // move操作可以理解為將左值轉變為右值返回 -- 是一個表達式return 0;
}

在這里插入圖片描述

通過上圖我們可以發現move操作是有風險的,它能將一個對象的資源轉交給另一個對象,轉移完之后這個對象就懸空了,我們不能對這個懸空的對象做操作!!當你不想用個資源了之后可以這樣做,如果后續你還要用到它就不能這么干了!!

我們來看看下面這種情況:

在這里插入圖片描述


C++11后,STL 中的容器都是增加了移動構造和移動賦值的。

在這里插入圖片描述

STL 容器的插入接口函數也增加了右值引用版本

在這里插入圖片描述

在這里插入圖片描述


總結:

  • 右值引用使用的場景:自定義類型臨時對象返回或自定義類型對象作為右值進行傳參時,此時編譯器會優先進行移動拷貝間接減少拷貝,將右值資源直接進行轉移!!
  • 左右值引用區別:左值引用是直接減少拷貝,而右值引用是間接減少拷貝,編譯器識別出是左值還是右值,如果是右值則不再進行深拷貝,直接進行移動拷貝轉移資源提高效率!!

1.3 右值引用引用左值及其一些更深入的使用場景分析

按照語法,右值引用只能引用右值,但右值引用一定不能引用左值嗎?因為:有些場景下,可能真的需要用右值去引用左值實現移動語義。當需要用右值引用引用一個左值時,可以通過move函數將左值轉化為右值。C++11中,std::move()函數位于頭文件中,該函數名字具有迷惑性,它并不搬移任何東西,唯一的功能就是將一個左值強制轉化為右值引用,然后實現移動語義。

template<class _Ty>
inline typename remove_reference<_Ty>::type&& move(_Ty&& _Arg) _NOEXCEPT
{
// forward _Arg as movable
return ((typename remove_reference<_Ty>::type&&)_Arg);
}
1.3.1 完美轉發

模板中的&& 萬能引用

// 下面的 Fun都是重載函數, 因為引用也屬于類型
void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }// 萬能引用:既能引用左值,也能引用右值
// 引用折疊
template<typename T>
void PerfectForward(T&& t)
{Fun(t);
}int main()
{PerfectForward(10); // 右值int a;PerfectForward(a); // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b); // const 左值PerfectForward(std::move(b)); // const 右值return 0;
}

模板中的&&不代表右值引用,而是萬能引用,其既能接收左值又能接收右值,這是大佬規定的語法。

我們輸出一下結果看看是否已經根據類型實現了萬能引用:

在這里插入圖片描述

我們發現結果并不是我們想象的那樣:左值引用就調用左值引用對應的函數,右值引用就調用右值引用的函數??

我們繼續進行分析,在之前的例子中我們說過右值是不能取地址的例如10,但是右值引用過后int&& rr1 = 10;這時候你會發現rr1此時成為了一個變量,既然是變量就有空間(地址),所以此時的rr1就充當了左值!!好,我們繼續來看一個例子:

在這里插入圖片描述

結論:模板的萬能引用只是提供了能夠接收同時接收左值引用和右值引用的能力,但是引用類型的唯一作用就是限制了接收的類型,后續使用中都退化成了左值,我們希望能夠在傳遞過程中保持它的左值或者右值的屬性, 就需要用我們下面學習的完美轉發!!


完美轉發:保持變量的屬性,防止提早轉變成左值屬性,導致調用的接口函數不一致。

在這里插入圖片描述

注:完美轉發操作在容器中的各種函數中十分常見,因為C++11提供了右值引用+移動語義,并且在某些場景下我們需要一直轉發才能防止變量屬性提早轉變(退化為左值)!!

我們來看看完美轉發的場景:

template<class T>
struct ListNode
{ListNode* _next = nullptr;ListNode* _prev = nullptr;T _data;
};template<class T>
class List
{typedef ListNode<T> Node;
public:List(){_head = new Node;_head->_next = _head;_head->_prev = _head;}void push_back(T&& x){//Insert(_head, x);Insert(_head, std::forward<T>(x));}void push_back(const T& x){Insert(_head, x);}void Insert(Node* pos, T&& x){Node* prev = pos->_prev;Node* newnode = new Node;newnode->_data = std::forward<T>(x); // 關鍵位置// prev newnode posprev->_next = newnode;newnode->_prev = prev;newnode->_next = pos;pos->_prev = newnode;}void Insert(Node* pos, const T& x){Node* prev = pos->_prev;Node* newnode = new Node;newnode->_data = x; // 關鍵位置// prev newnode posprev->_next = newnode;newnode->_prev = prev;newnode->_next = pos;pos->_prev = newnode;}
private:Node* _head;
};int main()
{List<curry::string> lt;lt.push_back("1111");lt.push_back(curry::string("hello"));return 0;
}

在這里插入圖片描述

我們來分析一下為什么會是這樣的結果:

在這里插入圖片描述

我們來看看每個接口函數間不一直進行轉發會發生什么情況:

在這里插入圖片描述

我們發現當insert函數中的x未進行轉發,導致提前退化為左值故調用賦值重載!!所以我們在某些場景下一定要小心將數據一直轉發下去!!

二、新的類功能

原來 C++ 類中,有 6 個默認成員函數:構造函數、析構函數、拷貝構造函數,賦值運算符重載、取地址重載和 const 取地址重載。重要的是前 4 個,后兩個用處不大。默認成員函數就是我們不寫編譯器會生成一個默認的。C++11 新增了兩個:移動構造函數和移動賦值運算符重載。拷貝構造函數和賦值運算符重載是針對左值的拷貝,而移動構造和移動賦值時針是右值的拷貝。不需要深拷貝的類,也就不需要自己寫移動構造和移動賦值。拷貝對象需要深拷貝時,自己寫移動構造和移動賦值。比如:string、vector 和 list 等。

針對移動構造函數和移動賦值運算符重載有一些需要注意的點如下:

  • 如果你沒有自己實現移動構造函數,且沒有實現析構函數 、拷貝構造、拷貝賦值重載中的任意一個。那么編譯器會自動生成一個默認移動構造。默認生成的移動構造函數,對于內置類型成員會執行淺拷貝,對于自定義類型成員,則需要看這個成員是否實現移動構造,如果實現了就調用移動構造,沒有實現就調用拷貝構造。
  • 如果你沒有自己實現移動賦值重載函數,且沒有實現析構函數 、拷貝構造、拷貝賦值重載中的任意一個,那么編譯器會自動生成一個默認移動賦值。默認生成的移動構造函數,對于內置類型成員會執行淺拷貝,自定義類型成員,則需要看這個成員是否實現移動賦值,如果實現了就調用移動賦值,沒有實現就調用賦值運算符重載。
  • 如果你提供了移動構造或者移動賦值,編譯器不會自動提供拷貝構造和拷貝賦值。如果沒有移動構造和移動賦值,才會去調用拷貝構造和賦值運算符重載。

類成員變量初始化

C++11允許在類定義時給成員變量初始缺省值,默認生成構造函數會使用這些缺省值初始化。這個在類和對象就講了,這里就不再細講了。

強制生成默認函數的關鍵字 default

C++11 可以讓你更好的控制要使用的默認函數。假設你要使用某個默認的函數,但是因為一些原因這個函數沒有默認生成。比如:我們提供了拷貝構造,就不會生成移動構造了,那么我們可以使用 default 關鍵字顯示指定移動構造生成。

禁止生成默認函數的關鍵字 delete

如果能想要限制某些默認函數的生成,在 C++98 中,可以將該函數設置成 private,這樣只要其他人想要調用就會報錯。在 C++11 中更簡單,只需在該函數聲明加上 = delete 即可,該語法指示編譯器不生成對應函數的默認版本,稱 = delete 修飾的函數為刪除函數。

繼承和多態中的 final 與 override 關鍵字

final 可以修飾一個類,表示這個類不能被繼承;也能修飾一個虛函數,表示這個虛函數不能被重寫。override 修飾子類的虛函數,如果子類的虛函數沒有完成重寫,就會編譯報錯。

三、可變參數模板

在這里插入圖片描述
其實在 C 語言中我們就已經接觸過可變參數了,只是我們并沒有深入的去研究過,上述printf的參數列表中... 就代表任意多個參數,這里我們就不去研究C語言中的可變參數模板到底是如何提取出參數的了,我們來看下C++是如何來操作的!!

C++11的新特性可變參數模板能夠讓您創建可以接受可變參數的函數模板和類模板,相比
C++98/03,類模版和函數模版中只能含固定數量的模版參數,可變模版參數無疑是一個巨大的改
進。然而由于可變模版參數比較抽象,使用起來需要一定的技巧,所以這塊還是比較晦澀的。現
階段呢,我們掌握一些基礎的可變參數模板特性就夠我們用了。

下面就是一個基本可變參數的函數模板

// Args是一個模板參數包,args是一個函數形參參數包
// 聲明一個參數包Args...args,這個參數包中可以包含0到任意個模板參數。
template <class ...Args>
void ShowList(Args... args)
{}

上面的參數 args 前面有省略號,所以它就是一個可變模版參數,我們把帶省略號的參數稱為參數包,它里面包含了 0 到 N(N>=0)個模版參數。我們無法直接獲取參數包 args 中的每個參數的,只能通過展開參數包的方式來獲取參數包中的每個參數,這是使用可變模版參數的一個主要特點,也是最大的難點,即如何展開可變模版參數。由于語法不支持使用 args[i] 這樣方式獲取可變參數,所以我們的用一些奇招來一一獲取參數包的值。

計算可變參數的個數

// 可變參數的模板
template <class ...Args>
void ShowList(Args... args)
{cout << sizeof...(args) << endl;
}int main()
{string str("hello");ShowList();ShowList(1);ShowList(1, 'A');ShowList(1, 'A', str);return 0;
}

在這里插入圖片描述

通過上圖我們能準確的知道每次調用showList有多少參數,但是我們該如何展開其中的參數包獲取參數包的值呢?

遞歸函數方式展開參數包

template <class T>
void ShowList(const T& t)
{cout << t << endl;
}template <class T, class ...Args>
void ShowList(T value, Args... args)
{cout << value << " ";ShowList(args...);
}int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

此時傳遞了兩個模板參數,第一個模板參數接收的是傳過來的值,第二個接收的是剩余的參數包,打印完value之后再繼續遞歸展開剩余的參數包,直至參數包的個數為0遞歸結束!!

在這里插入圖片描述

逗號表達式展開參數包
這種展開參數包的方式,不需要通過遞歸終止函數,是直接在expand函數體中展開的,printarg
不是一個遞歸終止函數,只是一個處理參數包中每一個參數的函數。

這種就地展開參數包的方式實現的關鍵是逗號表達式。我們知道逗號表達式會按順序執行逗號前面的表達式。
expand函數中的逗號表達式:(printarg(args), 0),也是按照這個執行順序,先執行
printarg(args),再得到逗號表達式的結果0。同時還用到了C++11的另外一個特性——初始化列
表,通過初始化列表來初始化一個變長數組, {(printarg(args), 0)…}將會展開成((printarg(arg1),0),
(printarg(arg2),0), (printarg(arg3),0), etc… ),最終會創建一個元素值都為0的數組int arr[sizeof…
(Args)]。由于是逗號表達式,在創建數組的過程中會先執行逗號表達式前面的部分printarg(args)
打印出參數,也就是說在構造int數組的過程中就將參數包展開了,這個數組的目的純粹是為了在
數組構造的過程展開參數包。

template <class T>
void PrintArg(T t)
{cout << t << " ";
}template <class ...Args>
void ShowList(Args... args)
{int arr[] = { (PrintArg(args), 0)... };cout << endl;
}int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

在這里插入圖片描述


STL 容器中的 empalce 相關接口函數

在這里插入圖片描述

我們可以看到C++11在很多容器中都新增了emplace相關的接口函數,并且emplace接口函數中使用了可變模板參數的萬能引用,那么相對于push_back()、insert等接口emplace有什么優勢的地方嗎?我們來看一組例子:

int main()
{list<curry::string> list;curry::string s1("1111");// 插入左值  -- 沒區別list.push_back(s1);list.emplace_back(s1);cout << endl;// 插入move()curry::string s2("22222");list.push_back(move(s1));list.emplace_back(move(s2));cout << endl;// 直接插入右值 -- 開始有區別//list.push_back("3333");//list.emplace_back("3333");return 0;
}

在這里插入圖片描述

下面我們來看看區別在哪?

在這里插入圖片描述

我們可以發現push_back比emplace_back多調用了一次移動構造將右值資源進行了轉移,那么為何emplace只會調用一次構造呢?原因是"3333"直接被當做const char*類型的參數包傳下去,最終直接去調用const char*的構造函數,而在push_back中"3333"要隱式類型轉換為string類型的對象,先去會去調用構造函數,然后它為右值類型會與移動構造進行匹配,所以它比emplace會多調用一次移動構造,但其實理論上倆者的效率都差不大多,因為需要深拷貝的類移動構造并不會消耗太多的時間!!


下面我們來看看淺拷貝類emplace_back與push_back的效率問題:

class Date
{
public:Date(int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){cout << "Date(int year = 1, int month = 1, int day = 1)" << endl;}Date(const Date& d):_year(d._year), _month(d._month), _day(d._day){cout << "Date(const Date& d)" << endl;}Date& operator=(const Date& d){cout << "Date& operator=(const Date& d))" << endl;return *this;}private:int _year;int _month;int _day;
};int main()
{// 沒有區別list<Date> list;Date d1(2024, 5, 18);list.push_back(d1);list.emplace_back(d1);cout << "-----------------" << endl;Date d2(2024, 5, 18);list.push_back(move(d1));list.emplace_back(move(d2));cout << "-----------------" << endl;// 有區別list.push_back(Date(2024, 5, 18));list.push_back({ 2024, 5, 18 });cout << "-----------------" << endl;list.emplace_back(Date(2024, 5, 18));list.emplace_back(2024, 5, 18);return 0;
}

注:由于我們實現的是淺拷貝類,所以移動構造與拷貝構造都是淺拷貝并無其他區別,這里我們就不去實現移動拷貝函數了。
在這里插入圖片描述

通過上圖我們知道emplace少調用了一次拷貝構造,當這個淺拷貝的類非常大時是不是就減少了大量的拷貝提高了效率,所以從這個角度新增emplace接口還是有一定價值的!!

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

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

相關文章

進程間通信的方式中,socket和消息隊列的區別

進程間通信的方式中&#xff0c;socket和消息隊列的區別 進程間通信方式中&#xff0c;socket和消息隊列的主要區別在于通信的方式和跨機通信的能力。 socket是通過網絡傳輸的方式來實現進程間通信&#xff0c;并且可以跨主機&#xff1b;而消息隊列是通過內核提供的緩沖區進…

Flutter 中的 AbsorbPointer 小部件:全面指南

Flutter 中的 AbsorbPointer 小部件&#xff1a;全面指南 在Flutter中&#xff0c;AbsorbPointer是一個特殊的小部件&#xff0c;用于吸收&#xff08;或“吞噬”&#xff09;所有傳遞到其子組件的指針事件&#xff08;如觸摸或鼠標點擊&#xff09;。這在某些情況下非常有用&…

民國漫畫雜志《時代漫畫》第22期.PDF

時代漫畫22.PDF: https://url03.ctfile.com/f/1779803-1248634856-2c7010?p9586 (訪問密碼: 9586) 《時代漫畫》的雜志在1934年誕生了&#xff0c;截止1937年6月戰爭來臨被迫停刊共發行了39期。 ps: 資源來源網絡!

Typescript高級: 深入理解Extract類型

概述 在TypeScript這一逐漸成為前端開發首選的靜態類型檢查語言中&#xff0c;類型系統提供了豐富的工具來幫助開發者編寫更加健壯和可維護的代碼。其中&#xff0c;Extract<T, U>是一個強大的內置實用類型&#xff0c;用于從一個聯合類型T中提取出屬于另一個類型U的那些…

AIGC 006-textual-inversion使用文本反轉實現個性化文本到圖像生成!

AIGC 006-textual-inversion使用文本反轉實現個性化文本到圖像生成&#xff01; 文章目錄 0 論文工作1 論文方法2 效果 0 論文工作 這篇論文 (An Image is Worth One Word: Personalizing Text-to-Image Generation using Textual Inversion) 提出了一種新穎的技術&#xff0c…

Modal.method() 不顯示頭部的問題

ant-design中的Modal組件有兩種用法&#xff1a; 第一種是用標簽&#xff1a;<a-modal></a-modal> 第二種是用Api&#xff1a;Modal.info、Modal.warning、Modal.confirm...... 一開始項目中這兩種用法是混用的&#xff0c;后面UI改造&#xff0c;需要統一樣式&…

一個程序員的牢獄生涯(37)任務

星期一 任 務 我走回大鐐面前后,把雙手抱著的衣服遞給大鐐,但我并沒有把手里的東西也遞給他。現在的大鐐坐著,我站著,這個時候要給大鐐的話,肯定能被身邊的棍子或六子看到,甚至被所有號子里的人都看到。因為此時,所有人的目光都盯著我手里的衣服,盯著我和大鐐看。 “鐐…

Shell字符串變量

目標 能夠使用字符串的3種方式 掌握Shell字符串拼接 掌握shell字符串截取的常用格式 能夠定義Shell索引數組和關聯數組 能夠使用內置命令alias,echo,read,exit,declare操作 掌握Shell的運算符操作 Shell字符串變量 介紹 字符串&#xff08;String&#xff09;就是一系…

使用LabVIEW時遇到VISA屬性錯誤 -1073807331的解決方案

在LabVIEW或VeriStand中使用VISA屬性時&#xff0c;可能會遇到錯誤 -1073807331。這一錯誤的具體描述如下&#xff1a; 解決方案 導致VISA屬性出現此錯誤的原因主要有以下四種&#xff1a; 屬性不被使用的串行總線支持 示例 A.1&#xff1a;Is Port Connected VISA屬性僅支持由…

React(四)memo、useCallback、useMemo Hook

目錄 (一)memo API 1.先想一個情景 2.用法 (1)props傳入普通數據類型的情況 (2)props傳入對象的情況 (3)props傳入函數的情況 (4)使用自定義比較函數 3.什么時候使用memo&#xff1f; (二)useMemo Hook 1.用法 2.useMemo實現組件記憶化 3.useMemo實現函數記憶化 …

如何停止 iPad 和 iPhone 之間共享短信,獨立接收和發送消息

概括 在當今高度互聯的數字世界中&#xff0c;Apple 設備之間的無縫連接性提供了極大的便利&#xff0c;尤其是在消息同步方面。iPhone 和 iPad 用戶通常可以享受到設備間短信的自動同步功能&#xff0c;這意味著無論是在哪個設備上&#xff0c;用戶都可以接收和回復消息。然而…

2024.5.26.python.exercise

# # 導入包 # from pyecharts.charts import Bar, Timeline # from pyecharts.options import LabelOpts, TitleOpts # from pyecharts.globals import ThemeType # # # 從文件中讀取信息 # GDP_file open("1960-2019全球GDP數據.csv", "r", encoding&quo…

A. Maximize?

time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output You are given an integer x&#x1d465;. Your task is to find any integer y&#x1d466; (1≤y<x)(1≤&#x1d466;<&#x1d465;) su…

深入理解python列表與字典:數據結構的選擇與性能差異

新書上架~&#x1f447;全國包郵奧~ python實用小工具開發教程http://pythontoolsteach.com/3 歡迎關注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目錄 一、列表與字典&#xff1a;基礎數據結構的對比 二、列表&#xff1a;逐個遍歷的查找方式 …

Ceres求解優化問題

1. 簡介 Ceres Solver是專門用于求解非線性最小二乘問題的C開源庫,研究SLAM方向不過濾波和優化兩個技術路線,因此常用Ceres庫解決實際項目中的優化問題,當然還有g2o同樣可用,但就說明文檔而言,Ceres對新用戶更友好,g2o提供不多的文檔,更多是需要參考其它開源項目使用,所以筆者…

【JAVA】接口

前面我們說了說抽象類相關內容&#xff0c;這篇我們主要聊聊接口相關內容&#xff0c;這部分很重要&#xff0c;大家引起關注。 1. 接口 1.1 接口的概念 接口就是公共的行為規范標準&#xff0c;大家在實現時&#xff0c;只要符合規范標準&#xff0c;就可以通用。在Java中&am…

力扣 739. 每日溫度 python AC

單調棧 class Solution:def dailyTemperatures(self, temperatures):size len(temperatures)ll []ans [0] * sizefor i in range(size - 1, -1, -1):while ll and temperatures[i] > temperatures[ll[-1]]:ll.pop()if ll:ans[i] ll[-1] - ill.append(i)return ans

C語言 數組——向函數傳遞數組

目錄 把數組傳給函數&#xff08;Passing Arrays to Functions&#xff09; 向函數傳遞一維數組 向函數傳遞二維數組 數組在學生成績管理中的應用 例&#xff1a;計算每個學生的平均分 把數組傳給函數&#xff08;Passing Arrays to Functions&#xff09; 向函數傳遞一維…

gnocchi學習小結

背景 總結gnocchi 4.4版本gnocchi-metricd工作流程 入口 gnocchi.cli.metricd metricd stop after processing metric默認為0&#xff0c;調servicemanager run MetricdServiceManager __init__ 服務邏輯封裝到MetricdServiceManager初始化中 主要由MetricProcessor, Met…

基于Vue的前端自定義詢問彈框與輸入彈框組件的設計與實踐

基于Vue的前端自定義詢問彈框與輸入彈框組件的設計與實踐 摘要 隨著技術的不斷進步&#xff0c;前端開發面臨越來越多的挑戰&#xff0c;其中之一就是如何有效管理復雜的業務邏輯和用戶體驗。傳統的整塊應用開發方式在面對頻繁的功能變更和用戶體驗優化時&#xff0c;往往顯得…