C++11新特性——移動語義,右值引用

移動語義

有一些類的資源是__不可共享__的,這種類型的對象可以被移動但不能被拷貝,如:IOunique_ptr
庫容器、stringshared_ptr 支持拷貝和移動,IOunique_ptr 則只能移動不能拷貝。。

右值引用

右值引用是必須綁定到右值的引用,右值引用使用 && 符號,相較于左值引用的& 。右值引用有一個特性就是其只能綁定到即將銷毀的對象上,因而,可以自由的移動右值引用對象中的資源。

左值表示對象的身份,而右值表示對象的值。不能將左值引用(lvalue reference)綁定到需要轉型的值、字面量或者返回右值的表達式上。右值引用則剛好相反:可以將右值引用綁定到以上的值,但不能直接將右值引用綁定到左值。如:

int i = 42;
int &r = i;
int &&rr = i; //錯誤:不能將右值引用綁定到左值上
int &r2 = i * 42; //錯誤:不能將左值引用綁定到右值上
const int &r3 = i * 42; //可以將 const 左值引用綁定到任何類型的值上(const/非 const 的左/右值)
int &&rr2 = i * 42; //將右值引用綁定到右值上

返回左值引用的函數和賦值、下標操作、解引用和前綴自增/自減操作符都是返回左值的表達式,可將左值引用綁定到這些表達式的結果中。

返回非引用類型的函數與算術、關系、位操作和后綴自增/自減的操作符都是返回右值的表達式,可將右值引用和 const 左值引用綁定到這種表達式上。

變量是左值

一個變量就是一個表達式,其只有一個操作數而沒有操作符。變量表達式是左值。因而,不能將右值引用綁定到一個定義為右值引用的變量上。如:

int &&rr1 = 42;
int &&rr2 = rr1; //錯誤:rr1 是左值,因而不能這樣定義

一個變量就是一個左值;不能直接將右值引用綁定到一個變量上,即使這個變量被定義為右值引用類型也不可以。

但是如果臨時對象通過一個接受右值的函數傳遞給另一個函數時,就會變成左值,因為這個臨時對象在傳遞過程中,變成了命名對象。

move庫函數

template< class T >                                                                                                                  (C++11 起)
typename std::remove_reference<T>::type&& move( T&& t ) noexcept;         (C++14 前)
template< class T >                                                                                                                  (C++14 起)
constexpr typename std::remove_reference<T>::type&& move( T&& t ) noexcept;

可以顯式將左值強轉為對應的右值引用類型,也可以通過調用 move 庫函數來獲取綁定到左值的右值引用,其被定義在 utility 頭文件中。如:

int &&rr3 = std::move(rr1);

調用 move 告知編譯器,以右值方式對象一個左值。特別需要了解的是調用 move 將承諾:不會再次使用 rr1 ,除非是賦值或者析構。當調用了 move 之后,不能對這個對象做任何值上的假設。可以析構或賦值給移動后的對象,但在此之前不能使用其值。

使用 move 的代碼應該使用 std::move ,而不是 move,這樣做可以避免潛在的名字沖突。

移動構造函數和移動賦值

為了讓我們自己的類可以執行移動操作,需要定義移動構造函數和移動賦值操作符。這些成員類似于對應的拷貝賦值操作,但是他們將從給定對象中偷取資源而不是復制。

  1. 參數(右值)不可以是常量,因為我們需要修改右值。
  2. 參數(右值)的資源鏈接和標記必須修改。否則,右值的析構函數就會釋放資源。轉移到新對象的資源也就無效了。

除了移動資源,移動構造函數需要保證移動后的對象的狀態是析構無害的。特別是,一旦資源被移動后,原始對象就不再指向移動了的資源,這些所有權被轉移給了新創建的對象。如:

StrVec::StrVec(StrVec &&s) noexcept :elements(s.elements), first_free(s.first_free), cap(s.cap)
{s.elements = s.first_free = s.cap = nullptr;
}

與拷貝構造函數不同,移動構造函數并不會分配新資源;其將攫取參數中的內存,在此之后,構造函數體將參數中的指針都設置為 nullptr,當一個對象被移動后,這個對象依然存在。最后移動后的對象將被析構,意味著析構函數將在此對象上運行。析構函數將釋放其所擁有的資源,如果沒有將指針設置為 nullptr 的,就會將移動了的資源給釋放掉

移動操作,庫容器和異常

移動操作通常不必自己分配資源,所以移動操作通常不拋出任何異常。當我們寫移動操作時,由于其不會拋出異常,我們應當告知編譯器這個事實。除非編譯器知道這個事實,它將必須做額外的工作來滿足移動構造操作將拋出異常。

通過在函數參數列表后加上 noexcept ,在構造函數時則,noexcept 出現在參數列表后到冒號之間,來告知編譯器一個函數不會拋出異常。如:

class StrVec {
public:StrVec(StrVec &&) noexcept;
};
StrVec::StrVec(StrVec &&s) noexcept : { ... }

必須同時在類體內的聲明處和定義處同時指定 noexcept

移動構造函數和移動賦值操作符,如果都不允許拋出異常,那么就應該被指定為 noexcept

告知移動操作不拋出異常是由于兩個不相關的事實:第一,盡管移動操作通常不拋出異常,它們可以這樣做。第二,有些庫容器在元素是否會在構建時拋出異常有不同的表現,如:vector 只有在知道元素類型的移動構造函數不會拋出異常才使用移動構造函數,否則將必須使用拷貝構造函數

移動賦值操作符

StrVec& StrVec::operator=(StrVec &&rhs) noexcept
{if (this == &rhs)return *this;free();elements = rhs.elements;first_free = rhs.first_free;cap = rhs.cap;rhs.elements = rhs.first_free = rhs.cap = nullptr;return *this;
}

移動賦值操作符不拋出異常應當用 noexcept 修飾,與拷貝賦值操作符一樣需要警惕自賦值的可能性。移動賦值操作符同時聚合了析構函數和移動構造函數的工作:其將釋放左操作數的內存,并且占有右操作數的內存,并將右操作數的指針設為 nullptr

移動后的對象必須是可以析構的

移動對象并不會析構那個對象,有時在移動操作完成后,被移動的對象將被銷毀。因而,當我們寫移動操作時,必須保證移動后的對象的狀態是可以析構的。StrVec 通過將其指針設置為 nullptr 來滿足此要求。

除了讓對象處于可析構狀態,移動操作必須保證對象處于有效狀態。通常來說,有效狀態就是可以安全的賦予新值或者使用在不依賴當前值的方式下。另一方面,移動操作對于遺留在移動后的對象中的值沒有什么特別要求,所以,程序不應該依賴于移動后對象的值

例如,從庫 string 和容器對象中移動資源后,移動后對象的狀態將保持有效。可以在移動后對象上調用 emptysize 函數,然而,并不保證得到的結果是空的。可以期望一個移動后對象是空的,但是這并不保證。

以上 StrVec 的移動操作將移動后對象留在一個與默認初始化一樣的狀態。因而,這個 StrVec 的所有操作將與默認初始化的 StrVec 的操作完全一樣。其它類,有著更加復雜的內部結構,也許會表現的不一致。

在移動后操作,移動后對象必須保證在一個有效狀態,并且可以析構,但是用戶不能對其值做任何假設

*合成移動操作

編譯器會為對象合成移動構造函數和移動賦值操作符。然而,在什么情況下合成移動操作與合成拷貝操作是十分不同的。

與拷貝操作不同的,對于某些類來說,編譯器根本不合成任何移動操作。特別是,如果一個類定義自己的拷貝構造函數、拷貝賦值操作符或析構函數,移動構造函數和移動賦值操作符是不會合成的。作為結果,有些類是沒有移動構造函數或移動賦值操作符。同樣,當一個類沒有移動操作時,對應的拷貝操作將通過函數匹配被用于替代移動操作。

編譯器只會在類沒有定義任何拷貝控制成員并且所有的非 static 數據成員都是可移動的情況下才會合成移動構造函數和移動賦值操作符。編譯器可以移動內置類型的成員,亦可以移動具有對應移動操作的類類型成員。

移動操作不會隱式被定義為刪除的,而是根本不定義,當沒有移動構造函數時,重載將選擇拷貝構造函數。當用 =default 要求編譯器生成時,如果編譯器無法移動所有成員,將會生成一個刪除的移動操作。被刪除的函數不是說不能被用于函數重載,而是說當其是重載解析時最合適的候選函數時,將是編譯錯誤。

  • 與拷貝構造函數不同,當類有一個成員定義了自己的拷貝構造函數,但是沒有定義移動構造函數時使用拷貝構造函數。當成員沒有定義自己的拷貝操作但是編譯器無法為其合成移動構造函數時,其移動構造函數被定義為被刪除的。對于移動賦值操作符是一樣的;
  • 如果類有一個成員其移動構造函數或移動賦值操作符是被刪除的或不可訪問的,其移動構造函數或移動賦值操作符被定義為被刪除的;
  • 與拷貝構造函數一樣,如果其析構函數是被刪除的或不可訪問的,移動構造函數被定義為被刪除的;
  • 與拷貝賦值操作符一樣,如果其有一個 const 或引用成員,移動賦值操作被定義為刪除的;

如果一個類定義自己的移動構造函數或移動賦值操作符,那么合成的拷貝構造函數和拷貝賦值操作符都將被定義為被刪除的。

右值移動,左值拷貝

當一個類既有移動構造函數又有拷貝構造函數,編譯器使用常規的函數匹配來決定使用哪個構造函數。拷貝構造函數通常使用 const StrVec 引用類型作為參數,因而,可以匹配可以轉為 StrVec 類型的對象參數。而移動構造函數則使用 StrVec && 作為參數,因而,只能使用非 const 的右值。如果調用拷貝形式的,需要將參數轉為 const 的,而移動形式的卻是精確匹配,因而,右值將調用移動形式的

右值在無法被移動時進行拷貝

如果一個類有拷貝構造函數,但是沒有定義移動構造函數,在這種情況下編譯不會合成移動構造函數,意味著類只有拷貝構造函數而沒有移動構造函數。如果一個類沒有移動構造函數,函數匹配保證即便是嘗試使用 move 來移動對象時,它們依然會被拷貝。

class Foo {
public:Foo() = default;Foo(const Foo&); //拷貝構造函數
};
Foo x;
Foo y(x);  //拷貝構造函數;x 是左值
Foo z(std::move(x)); //拷貝構造函數;因為沒有移動構造函數

調用 move(x) 時返回 Foo&&Foo 的拷貝構造函數是可行的,因為可以將 Foo&& 轉為 const Foo& ,因而,使用拷貝構造函數來初始化 z

使用拷貝構造函數來替換移動構造函數通常是安全的,對于賦值操作符來說是一樣的。拷貝構造符合移動構造函數的先決條件:它將拷貝給定的對象,并且不會改變其狀態,這樣原始對象將保持在有效狀態內。

拷貝和交換賦值操作與移動

class HasPtr {
public:HasPtr(HasPtr &&p) noexcept : ps(p.ps), i(p.i) {p.ps = 0;}HasPtr& operator=(HasPtr rhs){swap(*this, rhs);return *this;}
};

賦值操作符的參數是非引用類型的,所以參數是拷貝初始化的。根據參數的類型,拷貝初始化可能使用拷貝構造函數也可能使用移動構造函數。左值將被拷貝,右值將被移動。因而,這個移動操作符既是拷貝賦值操作符又是移動賦值操作符。如:

hp = hp2;
hp = std::move(hp2);

所有五個拷貝控制成員應該被當做一個整體:通常,如果一個類定義了其中任何一個操作,它通常需要定義所有成員。有些類必須定義拷貝構造函數,拷貝賦值操作符和析構函數才能正確工作。這種類通常有一個資源是拷貝成員必須拷貝的,通常拷貝資源需要做很多額外的工作,定義移動構造函數和移動賦值操作符可以避免在不需要拷貝的情況的額外工作。

移動迭代器

在新標準中,定義了移動迭代器(move iterator)適配器。移動迭代器通過改變迭代器的解引用操作來適配給定的迭代器。通常,迭代器解引用返回元素的左值引用,與其它迭代器不同,解引用移動迭代器返回右值引用。調用函數 make_move_iterator 將常規迭代器變成移動迭代器,移動迭代器的操作與原始迭代器操作基本一樣,因而可以將移動迭代器傳給 uninitialized_copy 函數。如:

uninitialized_copy(make_move_iterator(begin()), make_move_iterator(end()));

值得一提的是標準庫沒有說哪些算法可以使用移動迭代器,哪些不可以。因為移動對象會破壞原始對象,所以將移動迭代器傳給那些不會在移動后訪問其值的算法才合適。

慎用移動操作:由于移動后的對象處于中間狀態,在對象上調用 std::move 是很危險的。當調用 move 后,必須保證沒有別的用戶使用移動后對象。

謹慎克制的在類內使用 move 可以提供重大的性能提升,在用戶代碼中使用 move 則更可能導致難以定位的 bug,相比較得到的性能提升是不值得的。

在類實現代碼外使用 std::move,必須是在確實需要移動操作,并且保證移動是安全的。

右值引用和成員函數

除了構造函數和賦值操作符外提供拷貝和移動版本亦會受益。這種可以移動的成員函數中一個使用 const 左值引用,另一個使用非 const 右值引用。如:

void push_back(const X&); //拷貝:綁定到任何類型的 X
void push_back(X&&); //移動:綁定到可修改的右值 X

可以傳遞任何可以轉換為類型 X 的對象給拷貝版本,這個版本從參數中拷貝數據。只能將非 const 右值傳遞給移動版本。此版本比拷貝版本更好的匹配非 const 右值(精確匹配),因而,在函數匹配中將是更優的,并且可以自由的從參數中移動資源。

通常上面這種重載方式不會使用 const X&&X& 類型的參數,原因在于移動數據要求對象是非 const 的,而拷貝數據則應該是 const 的。

以拷貝或移動的方式對函數進行重載,常用的做法是一個版本使用 const T& 為參數,另外一個版本使用 T&& 為參數。

右值與左值引用的成員函數

有些成員函數是只允許左值調用的,右值是不能調用的,如:在新標準前可以給兩個字符串拼接的結果賦值:s1 + s2 = "wow!"; ,在新標準中可以強制要求賦值操作符的左操作數是左值,通過在參數列表后放置引用修飾符(reference qualifier)可以指示 this 的左值/右值特性。如:

class Foo {
public:Foo& operator=(const Foo&) &;
};
Foo& Foo::operator=(const Foo& rhs) &
{ return *this; }

引用修飾符可以是 & 或者 && 用于表示 this 指向左值或右值。與 const 修飾符一樣,引用修飾符必須出現在非 static 成員函數的聲明和定義處。被 & 修飾的函數只能被左值調用,被 && 修飾的函數只能被右值調用。

一個函數既可以有 const 也可以有引用修飾符,在這種情況下,引用修飾符在 const 修復符的后面。如:

class Foo {
public:Foo someMem() const &;
};

重載帶引用修飾符的成員函數

可以通過函數的引用修飾符進行重載,這與常規的函數重載是一樣的,&& 可以在可修改的右值上調用,const & 可以在任何類型的對象上調用。如:

class Foo {
public:Foo sorted() &&; //可以在可修改的右值上調用Foo sorted() const &; //可以在任何類型的 Foo 上調用
};

當定義具有相同名字和相同參數列表的成員函數時,必須同時提供引用修飾符或者都不提供引用修飾符,如果只在其中一些提供,而另外一些不提供就是編譯錯誤。如:

class Foo {
public:Foo sorted() &&;Foo sorted() const; //錯誤:必須提供引用修飾符//全不提供引用修飾符是合法的using Comp = bool(const int&, const int&);Foo sorted(Comp*);Foo sorted(Comp*) const;
};

精確傳遞 (Perfect Forwarding)

本文采用精確傳遞表達這個意思。Perfect Forwarding也被翻譯成完美轉發,精準轉發等,說的都是一個意思。

精確傳遞適用于這樣的場景:需要將一組參數原封不動的傳遞給另一個函數。

“原封不動”不僅僅是參數的值不變,在 C++ 中,除了參數值之外,還有一下兩組屬性:

左值/右值和 const/non-const。 精確傳遞就是在參數傳遞過程中,所有這些屬性和參數值都不能改變。在泛型函數中,這樣的需求非常普遍。

下面舉例說明。函數 forward_value 是一個泛型函數,它將一個參數傳遞給另一個函數 process_value

forward_value 的定義為:

template <typename T> void forward_value(const T& val) {process_value(val);
}
template <typename T> void forward_value(T& val) {process_value(val);
}

函數 forward_value 為每一個參數必須重載兩種類型,T&const T&,否則,下面四種不同類型參數的調用中就不能同時滿足 :

int a = 0;const int &b = 1;forward_value(a); // int&forward_value(b); // const int&
forward_value(2); // int&

對于一個參數就要重載兩次,也就是函數重載的次數和參數的個數是一個正比的關系。這個函數的定義次數對于程序員來說,是非常低效的。我們看看右值引用如何幫助我們解決這個問題 :

template <typename T> void forward_value(T&& val) {process_value(val);
}

只需要定義一次,接受一個右值引用的參數,就能夠將所有的參數類型原封不動的傳遞給目標函數。四種不用類型參數的調用都能滿足,參數的左右值屬性和 const/non-cosnt屬性完全傳遞給目標函數 process_value。這個解決方案不是簡潔優雅嗎?

int a = 0;
const int &b = 1;
forward_value(a); // int&
forward_value(b); // const int&
forward_value(2); // int&&

C++11 中定義的 T&& 的推導規則為:

右值實參為右值引用,左值實參仍然為左值引用。

一句話,就是參數的屬性不變。這樣也就完美的實現了參數的完整傳遞。

右值引用,表面上看只是增加了一個引用符號,但它對 C++ 軟件設計和類庫的設計有非常大的影響。它既能簡化代碼,又能提高程序運行效率。每一個 C++ 軟件設計師和程序員都應該理解并能夠應用它。我們在設計類的時候如果有動態申請的資源,也應該設計轉移構造函數和轉移拷貝函數。在設計類庫時,還應該考慮 std::move 的使用場景并積極使用它。

關鍵術語

  • 拷貝-交換(copy and swap):一種書寫賦值操作符的技術,先將右操作數拷貝到參數中,然后調用 swap 將其與左操作數進行交換;

  • 拷貝賦值操作符(copy-assignment operator):拷貝賦值操作符與本類的 const 引用對象作為參數,返回對象的引用。如果類不定義拷貝賦值操作符,編譯器將合成一個;

  • 拷貝構造函數(copy constructor):將新對象初始化為本類的另一個對象的副本的構造函數。拷貝構造函數將在以非引用方式傳遞參數或從函數中返回時默認調用。如果類不定義的話,編譯器將合成一個;

  • 拷貝控制(copy control):用于控制對象被拷貝、移動、賦值和銷毀時應當做什么的成員函數。如果類不定這些函數,編譯器將在合適的時候合成它們;

  • 拷貝初始化(copy initialization):使用 = 形式的初始化,或者當傳遞參數、按值形式返回值,或者初始化數組或聚合類時,將進行拷貝初始化。拷貝初始化將根據初始值是左值還是右值,使用拷貝構造函數或者移動構造函數;

  • 被刪除的函數(deleted function):不被使用的函數,通過 =delete 來刪除函數。使用被刪除的函數是告知編譯器在進行函數匹配時,如果匹配到被刪除的函數就報編譯器錯誤;

  • 析構函數(destructor):當對象離開作用域時調用的特殊成員函數來清理對象。編譯器自動銷毀每個數據成員,類成員通過調用其析構函數進行銷毀,內置類型或符合類型將不做任何析構操作,特別是指向動態對象的指針不會被自動 delete

  • 逐個成員拷貝/賦值(memberwise copy/assign):合成的拷貝/移動構造函數和拷貝/移動賦值操作符的運作方式。依次對所有的數據成員,拷貝/移動構造函數通過從參數中拷貝/移動對應的成員進行初始化;拷貝/移動賦值操作符則依次對右操作數的各個成員進行拷貝/移動賦值;內置類型的成員是直接進行初始化或賦值的。類類型成員則調用對應的拷貝/移動構造函數或拷貝/移動賦值操作符;

  • move 函數(move function):用于將左值綁定到右值引用的庫函數。調用 move 將隱式保證不會使用移動后的對象值,唯一的操作是析構或者賦予新值;

  • 移動賦值操作符(move-assignment operator):參數是右值引用的賦值操作符版本。通常移動賦值操作符將其右操作數的數據移動到左操作數。在賦值后,必須保證可以安全的析構掉右操作數;

  • 移動構造函數(move constructor):以右值引用為參數的構造函數。移動構造函數將參數中的數據移動到新創建的對象中。在移動后,必須保證可以安全地析構掉右操作數;

  • 移動迭代器(move iterator):迭代器適配器,包裝一個迭代器,當其解引用時返回右值引用;

  • 右值引用(rvalue reference):對即將被銷毀的對象的引用;

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

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

相關文章

離合器半聯動點的判斷和技巧 為您支招

現在將離合器半聯動的使用方法揭密如下&#xff1a;將離合器抬到車開始動時你就別再抬了&#xff0c;你如果感覺到車有些快了&#xff0c;可再往下踩些&#xff0c;你如果感覺到車有些慢了&#xff0c;可再往起抬些&#xff0c;這樣可將車速控制在你想要的速度范圍之內。 ● 坡…

客戶端調用 WCF 的幾種方式

轉載網絡代碼.版權歸原作者所有.....客戶端調用&#xff37;&#xff23;&#xff26;的幾種常用的方式&#xff1a;&#xff11;普通調用var factory new DataContent.ServiceReference1.CustomerServiceClient();factory.Open();List<DataContent.ServiceReference1.Cust…

VUE:組件間相互跳轉、頁面帶參跳轉

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 只是記錄下用法&#xff1a; 從 A 頁面跳轉到 B 頁面。 如下寫法&#xff1a; A 頁面跳轉方式&#xff1a; 代碼&#xff1a; getdat…

Protocol Buffer 序列化

Protobuf使用 目錄 proto3的更新定義協議格式編譯protobufprotobuf_API 枚舉和嵌套類標準消息方法解析和序列化 寫一條消息閱讀消息編譯Protobuf擴展優化高級用法 proto3的更新 在第一行非空白非注釋行&#xff0c;必須寫&#xff1a; syntax "proto3";字段規…

如何調整反光鏡和座椅的位置 為您支招

【太平洋汽車網 學車頻道】首先要進行座椅的高度調整&#xff0c;上下調整座椅讓頭部離車頂至少還有一拳的距離。如果座椅調得太高&#xff0c;車輛在顛簸時頭部容易碰到車頂&#xff0c;調得太矮了又會影響視線。然后是前后距離的調整&#xff0c;當腳踩住制動踏板至最深處時…

關于hexo與github使用過程中的問題與筆記

快速閱讀 如何用github 和hexo 創建一個blog 1.github中要新建一個與用戶名同一樣的倉庫&#xff0c; 如:homehe.github.io - 必須是io后綴。一個帳戶 只能建立一個2. 綁定域名 &#xff0c; A記錄指向ip, cname記錄指向homehe.github.io 3. 配置sshkey - 個人設置 -> SSH a…

CSS 中 的 margin、border、padding 區別 (內邊距、外邊距)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 圖解CSS padding、margin、border屬性 W3C組織建議把所有網頁上的對像都放在一個盒(box)中&#xff0c;設計師可以通過創建定義來控制這…

CMake 常用的預定義變量

CMake 常用的預定義變量 PROJECT_NAME : 通過 project() 指定項目名稱 PROJECT_SOURCE_DIR : 工程的根目錄 PROJECT_BINARY_DIR : 執行 cmake 命令的目錄 CMAKE_CURRENT_SOURCE_DIR : 當前 CMakeList.txt 文件所在的目錄 CMAKE_CURRENT_BINARY_DIR : 編譯目錄&#xff0c;…

什么是轉向燈?使用轉向燈有何技巧?

什么是轉向燈&#xff1f;如何使用轉向燈&#xff1f;新手司機對車輛還不是很熟悉&#xff0c;如何正確使用轉向燈&#xff0c;尤其是在不同路段中該怎么正確使用轉向燈&#xff0c;成為了很多新手們的困擾之一&#xff0c;今天我們就來為大家解決這個問題吧&#xff01; 轉向燈…

基于Flask開發企業級REST API應用(一)

關于我 編程界的一名小小程序猿&#xff0c;目前在一個創業團隊任team lead&#xff0c;技術棧涉及Android、Python、Java和Go&#xff0c;這個也是我們團隊的主要技術棧。 Github&#xff1a;github.com/hylinux1024 微信公眾號&#xff1a;angrycode 前面對Python WEB框架Fla…

解決:Do not use built-in or reserved HTML elements as component id: form

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. vue 新寫了個組件&#xff0c;運行工程成功&#xff0c;但界面沒有出效果&#xff0c;F12 提示有一個警告&#xff1a; Do not use …

移動語義,右值引用

移動語義 目錄 右值引用變量是左值move庫函數移動構造函數和移動賦值移動操作庫容器和異常移動賦值操作符移動后的對象必須是可以析構的合成移動操作右值移動左值拷貝右值在無法被移動時進行拷貝拷貝和交換賦值操作與移動移動迭代器右值引用和成員函數右值與左值引用的成員函…

集合練習:登錄注冊功能

需求&#xff1a; 1、登錄賬號唯一&#xff0c;在注冊時驗證輸入的賬號是否可用&#xff0c;若已存在&#xff0c;則不可用&#xff0c;若不存在則可用2、登錄時使用賬號密碼進行驗證1 /**2 * author Administrator3 * 登錄信息 4 */5 public class UserLogin {6 …

vue 通信、傳值的多種方式(超詳細)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 一、通過路由帶參數進行傳值 ①兩個組件 A和B,A組件通過query把orderId傳遞給B組件&#xff08;觸發事件可以是點擊事件、鉤子函數等&am…

新手開車 駕駛小秘訣要牢記

有很多人剛買到新車興奮異常&#xff0c;憑著并不熟練的駕駛技術&#xff0c;過了幾天的車癮后發現&#xff0c;剛買的車怎么出現了這樣那樣的問題 有很多人剛買到新車興奮異常&#xff0c;憑著并不熟練的駕駛技術&#xff0c;過了幾天的車癮后發現&#xff0c;剛買的車怎么出現…

chrome中Google插件導出導入

導出插件&#xff1a; 一般電腦默認將你安裝的插件存放的位置在&#xff1a;C:\Users&#xff08;用戶&#xff09;\你的電腦名稱\AppData\Local\Google\Chrome\User Data\Default\Extensions 這個文件夾下。這里的 AppData 是個隱藏文件夾&#xff0c;需要顯示隱藏文件夾才行…

科目三電子路考操作流程

如果你已經通過科目二場內五項考試&#xff0c;正準備參加科目三電子路考的話&#xff0c;不妨看看由邕江駕校李師傅操作講解的電子路考考試流程演示視頻&#xff0c;每項都有詳細介紹操作要領及評判標準哦。 科目三考試項目&#xff1a;上車準備、起步、路口左轉彎、通過學校區…

vue Bus 總線 組件間通信

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 有時候兩個組件也需要通信&#xff08;非父子關系&#xff09;。當然Vue2.0提供了Vuex&#xff0c;但在簡單的場景下&#xff0c;可以使…

C++沒有調用析構函數

github地址 在項目中遇到一個問題&#xff0c;析構函數沒有調用產生了內存泄露。 具體見valgrind檢測libevent內存泄露 我們看兩個例子 demo1 class Test1; void del(Test1* obj){delete obj; } class Test1{ public:Test1(){printf("Test1\r\n");}~Test1(){pri…

實際操作之路考的這些事

辛苦了這么久練習路考&#xff0c;今天終于實際操作到我路考了。上車以后。關上車門。把考試的單地上給考官。還沒有認真的去看考官一眼。于是就聽到考官用低沉的聲音對我說&#xff1a;你好&#xff01;當時就感覺有一點意外。沒想到考官你這么有禮貌。然后我就沒那么緊張了&a…