色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

一文詳解C++中的移動語義

Linux愛好者 ? 來源:高性能架構探索 ? 作者:雨樂 ? 2022-06-12 14:56 ? 次閱讀

一直以來,C++中基于值語義的拷貝和賦值嚴重影響了程序性能。尤其是對于資源密集型對象,如果進行大量的拷貝,勢必會對程序性能造成很大的影響。

為了盡可能的減小因為對象拷貝對程序的影響,開發人員使出了萬般招式:盡可能的使用指針、引用。而編譯器也沒閑著,通過使用RVO、NRVO以及復制省略技術,來減小拷貝次數來提升代碼的運行效率。

但是,對于開發人員來說,使用指針和引用不能概括所有的場景,也就是說仍然存在拷貝賦值等行為;對于編譯器來說,而對于RVO、NRVO等編譯器行為的優化需要滿足特定的條件(具體可以參考文章編譯器之返回值優化)。

為了解決上述問題,自C++11起,引入了移動語義,更進一步對程序性能進行優化 。

C++11新標準重新定義了lvalue和rvalue,并允許函數依照這兩種不同的類型進行重載。通過對于右值(rvalue)的重新定義,語言實現了移動語義(move semantics)和完美轉發(perfect forwarding),通過這種方法,C++實現了在保留原有的語法并不改動已存在的代碼的基礎上提升代碼性能的目的。

本文的主要內容如下圖所示:

418232e6-ea04-11ec-ba43-dac502259ad0.png

值語義

值語義(value semantics)指目標對象由源對象拷貝生成,且生成后與源對象完全無關,彼此獨立存在,改變互不影響,就像int類型互相拷貝一樣。C++的內置類型(bool/int/double/char)都是值語義,標準庫里的complex<> 、pair<>、vector<>、map<>、string等等類型也都是值語意,拷貝之后就與原對象脫離關系。

C++中基于值語義的拷貝構造和賦值拷貝,會招致對資源密集型對象不必要拷貝,大量的拷貝很可能成為程序的性能瓶頸。

首先,我們看一段例子:

BigObjfun(BigObjobj){
BigObjo;
//dosth
returno;
}

intmain(){
fun(BigObj());
return0;
}

在上述代碼中,我們定義了一個函數fun()其參數是一個BigObj對象,當調用fun()函數時候,會通過調用BigObj的拷貝構造函數,將obj變量傳遞給fun()的參數。

編譯器知道何時調用拷貝構造函數或者賦值運算符進行值傳遞。如果涉及到底層資源,比如內存、socket等,開發人在定義類的時候,需要實現自己的拷貝構造和賦值運算符以實現深拷貝。然而拷貝的代價很大,當我們使用STL容器的時候,都會涉及到大量的拷貝操作,而這些會浪費CPU和內存等資源。

正如上述代碼中所示的那樣,當我們將一個臨時變量(BigObj(),也稱為右值)傳遞給一個函數的時候,就會導致拷貝操作,那么我們該如何避免此種拷貝行為呢?這就是我們本文的主題:移動語義

左值、右值

關于左值、右值,我們在之前的文章中已經有過詳細的分享,有興趣的同學可以移步【Modern C++】深入理解左值、右值,在本節,我們簡單介紹下左值和右值的概念,以方便理解下面的內容。

左值(lvalue,left value),顧名思義就是賦值符號左邊的值。準確來說,左值是表達式結束(不一定是賦值表達式)后依然存在的對象。

可以將左值看作是一個關聯了名稱的內存位置,允許程序的其他部分來訪問它。在這里,我們將 "名稱" 解釋為任何可用于訪問內存位置的表達式。所以,如果 arr 是一個數組,那么 arr[1] 和 *(arr+1) 都將被視為相同內存位置的“名稱”。

左值具有以下特征:

  • 可通過取地址運算符獲取其地址
  • 可修改的左值可用作內建賦值和內建符合賦值運算符的左操作數
  • 可以用來初始化左值引用(后面有講)

C++11將右值分為純右值將亡值兩種。純右值就是C++98標準中右值的概念,如非引用返回的函數返回的臨時變量值;一些運算表達式,如1+2產生的臨時變量;不跟對象關聯的字面量值,如2,'c',true,"hello";這些值都不能夠被取地址。

而將亡值則是C++11新增的和右值引用相關的表達式,這樣的表達式通常是將要移動的對象、T&&函數返回值、std::move()函數的返回值等。

左值引用、右值引用

在明確了左值和右值的概念之后,我們將在本節簡單介紹下左值引用和右值引用。

按照概念,對左值的引用稱為左值引用,而對右值的引用稱為右值引用。既然有了左值引用和右值引用,那么在C++11之前,我們通常所說的引用又是什么呢?其實就是左值引用,比如:

inta=1;
int&b=a;

在C++11之前,我們通過會說b是對a的一個引用(當然,在C++11及以后也可以這么說,大家潛移默化的認識就是引用==左值引用),但是在C++11中,更為精確的說法是b是一個左值引用。

在C++11中,為了區分左值引用,右值引用用&&來表示,如下:

int&&a=1;//a是一個左值引用
intb=1;
int&&c=b;//錯誤,右值引用不能綁定左值

跟左值引用一樣,右值引用不會發生拷貝,并且右值引用等號右邊必須是右值,如果是左值則會編譯出錯,當然這里也可以進行強制轉換,這將在后面提到。

在這里,有一個大家都經常容易犯的一個錯誤,就是綁定右值的右值引用,其變量本身是個左值。為了便于理解,代碼如下:

intfun(int&a){
std::cout<"infun(int&)"<std::endl;
}

intfun(int&&a){
std::cout<"infun(int&)"<std::endl;
}

intmain(){
inta=1;
int&&b=1;

fun(b);

return0;
}

代碼輸出如下:

infun(int&)

左值引用和右值引用的規則如下:

  • 左值引用,使用T&,只能綁定左值
  • 右值引用,使用T&&,只能綁定右值
  • 常量左值,使用const T&,既可以綁定左值,又可以綁定右值,但是不能對其進行修改
  • 具名右值引用,編譯器會認為是個左值
  • 編譯器的優化需要滿足特定條件,不能過度依賴

好了,截止到目前,相信你對左值引用和右值引用的概念有了初步的認識,那么,現在我們介紹下為什么要有右值引用呢?我們看下述代碼:

BigObjfun(){
returnBigObj();
}
BigObjobj=fun();//C++11以前
BigObj&&obj=fun();//C++11

上述代碼中,在C++11之前,我們只能通過編譯器優化(N)RVO的方式來提升性能,如果不滿足編譯器的優化條件,則只能通過拷貝等方式進行操作。自C++11引入右值引用后,對于不滿足(N)RVO條件,也可以通過避免拷貝延長臨時變量的生命周期,進而達到優化的目的。

但是僅僅使用右值引用還不足以完全達到優化目的,畢竟右值引用只能綁定右值。那么,對于左值,我們又該如何優化呢?是否可以通過左值轉成右值,然后進行優化呢?等等

為了解決上述問題,標準引入了移動語義。通移動語義,可以在必要的時候避免拷貝;標準提供了move()函數,可以將左值轉換成右值。接下來,就開始我們本文的重點-移動語義。

移動語義

移動語義是Howard Hinnant在2002年向C++標準委員會提議的,引用其在移動語義提案上的一句話:

移動語義不是試圖取代復制語義,也不是以任何方式破壞它。相反,該提議旨在增強復制語義

對于剛剛接觸移動語義的開發人員來說,很難理解為什么有了值語義還需要有移動語義。

我們可以想象一下,有一輛汽車,在內置發動機的情況下運行平穩,有一天,在這輛車上安裝了一個額外的V8發動機。當有足夠燃料的時候,V8發動機就能進行加速。所以,汽車是值語義,而V8引擎則是移動語義。在車上安裝引擎不需要一輛新車,它仍然是同一輛車,就像移動語義不會放棄值語義一樣。

所以,如果可以,使用移動語義,否則使用值語義,換句話說就是,如果燃料充足,則使用V8引擎,否則使用原始默認引擎。

好了,截止到現在,我們對移動語義有一個感官上的認識,它屬于一種優化,或者說屬于錦上添花。再次引用Howard Hinnant在移動語義提案上的一句話:

移動語義主要是性能優化:將昂貴的對象從內存中的一個地址移動到另外一個地址的能力,同時竊取源資源以便以最小的代價構建目標

在C++11之前,當進行值傳遞時,編譯器會隱式調用拷貝構造函數;自C++11起,通過右值引用來避免由于拷貝調用而導致的性能損失。

右值引用的主要用途是創建移動構造函數和移動賦值運算符。移動構造函數和拷貝構造函數一樣,將對象的實例作為其參數,并從原始對象創建一個新的實例。

但是,移動構造函數可以避免內存重新分配,這是因為移動構造函數的參數是一個右值引用,也可以說是一個臨時對象,而臨時對象在調用之后就被銷毀不再被使用,因此,在移動構造函數中對參數進行移動而不是拷貝。換句話說,右值引用移動語義允許我們在使用臨時對象時避免不必要的拷貝。

移動語義通過移動構造函數移動賦值操作符實現,其與拷貝構造函數類似,區別如下:

  • 參數的符號必須為右值引用符號,即為&&
  • 參數不可以是常量,因為函數內需要修改參數的值
  • 參數的成員轉移后需要修改(如改為nullptr),避免臨時對象的析構函數將資源釋放掉

為了方便我們理解,下面代碼包含了完整的移動構造和移動運算符,如下:

classBigObj{
public:
explicitBigObj(size_tlength)
:length_(length),data_(newint[length]){
}

//Destructor.
~BigObj(){
if(data_!=NULL){
delete[]data_;
length_=0;
}
}

//拷貝構造函數
BigObj(constBigObj&other)
:length_(other.length_),data(newint[other.length_]){
std::copy(other.mData,other.mData+mLength,mData);
}

//賦值運算符
BigObj&operator=(constBigObj&other){
if(this!=&other;){
delete[]data_;
length_=other.length_;
data_=newint[length_];
std::copy(other.data_,other.data_+length_,data_);
}
return*this;
}

//移動構造函數
BigObj(BigObj&&other):data_(nullptr),length_(0){
data_=other.data_;
length_=other.length_;

other.data_=nullptr;
other.length_=0;
}

//移動賦值運算符
BigObj&operator=(BigObj&&other){
if(this!=&other;){
delete[]data_;

data_=other.data_;
length_=other.length_;

other.data_=NULL;
other.length_=0;
}
return*this;
}

private:
size_tlength_;
int*data_;
};

intmain(){
std::vectorv;
v.push_back(BigObj(25));
v.push_back(BigObj(75));

v.insert(v.begin()+1,BigObj(50));
return0;
}

移動構造

移動構造函數的定義如下:

BigObj(BigObj&&other):data_(nullptr),length_(0){
data_=other.data_;
length_=other.length_;

other.data_=nullptr;
other.length_=0;
}

從上述代碼可以看出,它不分配任何新資源,也不會復制其它資源:other中的內存被移動到新成員后,other中原有的內容則消失了。換句話說,它竊取了other的資源,然后將other設置為其默認構造的狀態。

在移動構造函數中,最最關鍵的一點是,它沒有額外的資源分配,僅僅是將其它對象的資源進行了移動,占為己用。

在此,我們假設data_很大,包含了數百萬個元素。如果使用原來拷貝構造函數的話,就需要將該數百萬元素挨個進行復制,性能可想而知。而如果使用該移動構造函數,因為不涉及到新資源的創建,不僅可以節省很多資源,而且性能也有很大的提升。

移動賦值運算符

代碼如下:

BigObj&operator=(constBigObj&other){
if(this!=&other;){
delete[]data_;
length_=other.length_;
data_=newint[length_];
std::copy(other.data_,other.data_+length_,data_);
}
return*this;
}

移動賦值運算符的寫法類似于拷貝賦值運算符,所不同點在于:移動賦值預算法會破壞被操作的對象(上述代碼中的參數other)。

移動賦值運算符的操作步驟如下:

  1. 釋放當前擁有的資源
  2. 竊取他人資源
  3. 將他人資源設置為默認狀態
  4. 返回*this

在定義移動賦值運算符的時候,需要進行判斷,即被移動的對象是否跟目標對象一致,如果一致,則會出問題,如下代碼:

data=std::move(data);

在上述代碼中,源和目標是同一個對象,這可能會導致一個嚴重的問題:它最終可能會釋放它試圖移動的資源。為了避免此問題,我們需要通過判斷來進行,比如可以如下操作:

if(this==&other){
return*this
}

生成時機

眾所周知,在C++中有四個特殊的成員函數:默認構造函數、析構函數,拷貝構造函數,拷貝賦值運算符。

之所以稱之為特殊的成員函數,這是因為如何開發人員沒有定義這四個成員函數,那么編譯器則在滿足某些特定條件(僅在需要的時候才生成,比如某個代碼使用它們但是它們沒有在類中明確聲明)下,自動生成。這些由編譯器生成的特殊成員函數是public且inline。

自C++11起,引入了另外兩只特殊的成員函數:移動構造函數和移動賦值運算符。如果開發人員沒有顯示定義移動構造函數和移動賦值運算符,那么編譯器也會生成默認。

與其他四個特殊成員函數不同,編譯器生成默認的移動構造函數和移動賦值運算符需要,滿足以下條件:

  • 如果一個類定義了自己的拷貝構造函數,拷貝賦值運算符或者析構函數(這三者之一,表示程序員要自己處理對象的復制或釋放問題),編譯器就不會為它生成默認的移動構造函數或者移動賦值運算符,這樣做的目的是防止編譯器生成的默認移動構造函數或者移動賦值運算符不是開發人員想要的
  • 如果類中沒有提供移動構造函數和移動賦值運算符,且編譯器不會生成默認的,那么我們在代碼中通過std::move()調用的移動構造或者移動賦值的行為將被轉換為調用拷貝構造或者賦值運算符
  • 只有一個類沒有顯示定義拷貝構造函數、賦值運算符以及析構函數,且類的每個非靜態成員都可以移動時,編譯器才會生成默認的移動構造函數或者移動賦值運算符
  • 如果顯式聲明了移動構造函數或移動賦值運算符,則拷貝構造函數和拷貝賦值運算符將被隱式刪除(因此程開發人員必須在需要時實現拷貝構造函數和拷貝賦值運算符)

與拷貝操作一樣,如果開發人員定義了移動操作,那么編譯器就不會生成默認的移動操作,但是編譯器生成移動操作的行為和生成拷貝操作的行為有些許不同,如下:

  • 兩個拷貝操作是獨立的:聲明一個不會限制編譯器生成另一個。所以如果你聲明一個拷貝構造函數,但是沒有聲明拷貝賦值運算符,如果寫的代碼用到了拷貝賦值,編譯器會幫助你生成拷貝賦值運算符。

    同樣的,如果你聲明拷貝賦值運算符但是沒有拷貝構造函數,代碼用到拷貝構造函數時編譯器就會生成它。上述規則在C++98和C++11中都成立。
  • 兩個移動操作不是相互獨立的。如果你聲明了其中一個,編譯器就不再生成另一個。如果你給類聲明了,比如,一個移動構造函數,就表明對于移動操作應怎樣實現,與編譯器應生成的默認逐成員移動有些區別。

    如果逐成員移動構造有些問題,那么逐成員移動賦值同樣也可能有問題。所以聲明移動構造函數阻止編譯器生成移動賦值運算符,聲明移動賦值運算符同樣阻止編譯器生成移動構造函數。

類型轉換-move()函數

在前面的文章中,我們提到,如果需要調用移動構造函數和移動賦值運算符,就需要用到右值。那么,對于一個左值,又如何使用移動語義呢?自C++11起,標準庫提供了一個函數move()用于將左值轉換成右值。

首先,我們看下cppreference中對move語義的定義:

std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object.

In particular, std::move produces an xvalue expression that identifies its argument t. It is exactly equivalent to a static_cast to an rvalue reference type.

從上述描述,我們可以理解為std::move()并沒有移動任何東西,它只是進行類型轉換而已,真正進行資源轉移的是開發人員實現的移動操作

該函數在STL中定義如下:

template<typename_Tp>
constexprtypenamestd::remove_reference<_Tp>::type&&
move(_Tp&&__t)noexcept
{returnstatic_cast<typenamestd::remove_reference<_Tp>::type&&>(__t);}

從上面定義可以看出,std::move()并不是什么黑魔法,而只是進行了簡單的類型轉換:

  • 如果傳遞的是左值,則推導為左值引用,然后由static_cast轉換為右值引用
  • 如果傳遞的是右值,則推導為右值引用,然后static_cast轉換為右值引用

使用move之后,就意味著兩點:

  • 原對象不再被使用,如果對其使用會造成不可預知的后果
  • 所有權轉移,資源的所有權被轉移給新的對象

使用

在某些情況下,編譯器會嘗試隱式移動,這意味著您不必使用std::move()。只有當一個非常量的可移動對象被傳遞、返回或賦值,并且即將被自動銷毀時,才會發生這種情況。

自c++11起,開始支持右值引用。標準庫中很多容器都支持移動語義,以std::vector<>為例,**vector::push_back()**定義了兩個重載版本,一個像以前一樣將const T&用于左值參數,另一個將T&&類型的參數用于右值參數。如下代碼:

intmain(){
std::vectorv;
v.push_back(BigObj(10));
v.push_back(BigObj(20));

return0;
}

兩個push_back()調用都將解析為push_back(T&&),因為它們的參數是右值。push_back(T&&)使用BigObj的移動構造函數將資源從參數移動到vector的內部BigObj對象中。而在C++11之前,上述代碼則生成參數的拷貝,然后調用BigObj的拷貝構造函數。

如果參數是左值,則將調用push_back(T&):

intmain(){
std::vectorv;
BigObjobj(10);
v.push_back(obj);//此處調用push_back(T&)

return0;
}

對于左值對象,如果我們想要避免拷貝操作,則可以使用標準庫提供的move()函數來實現(前提是類定義中實現了移動語義),代碼如下:

intmain(){
std::vectorv;
BigObjobj(10);
v.push_back(std::move(obj));//此處調用push_back(T&&)

return0;
}

我們再看一個常用的函數swap(),在使用移動構造之前,我們定義如下:

template
voidswap(T&a,T&b){
Ttemp=a;//調用拷貝構造函數
a=b;//調用operator=
b=temp;//調用operator=
}

如果T是簡單類型,則上述轉換沒有問題。但如果T是含有指針的復合數據類型,則上述轉換中會調用一次復制構造函數,兩次賦值運算符重載。

41d44e78-ea04-11ec-ba43-dac502259ad0.png

而如果使用move()函數后,則代碼如下:

template
voidswap(T&a,T&b){
Ttemp=std::move(a);
a=std::move(b);
b=std::move(temp);
}

與傳統的swap實現相比,使用move()函數的swap()版本減少了拷貝等操作。如果T是可移動的,那么整個操作將非常高效。如果它是不可移動的,那么它和普通的swap函數一樣,調用拷貝和賦值操作,不會出錯,且是安全可靠的。

41f97ff4-ea04-11ec-ba43-dac502259ad0.png

經驗之談

對int等基礎類型進行move()操作,不會改變其原值

對于所有的基礎類型-int、double、指針以及其它類型,它們本身不支持移動操作(也可以說本身沒有實現移動語義,畢竟不屬于我們通常理解的對象嘛),所以,對于這些基礎類型進行move()操作,最終還是會調用拷貝行為,代碼如下:

intmain()
{
inta=1;
int&&b=std::move(a);

std::cout<"a="<std::endl;
std::cout<"b="<std::endl;

return0;
}

最終結果輸出如下:

a=1
b=1

move構造或者賦值函數中,請將原對象恢復默認值

我們看如下代碼:

classBigObj{
public:
explicitBigObj(size_tlength)
:length_(length),data_(newint[length]){
}

//Destructor.
~BigObj(){
if(data_!=NULL){
delete[]data_;
length_=0;
}
}

//拷貝構造函數
BigObj(constBigObj&other)=default;

//賦值運算符
BigObj&operator=(constBigObj&other)=default;

//移動構造函數
BigObj(BigObj&&other):data_(nullptr),length_(0){
data_=other.data_;
length_=other.length_;
}

private:
size_tlength_;
int*data_;
};

intmain(){
BigObjobj(1000);
BigObjo;
{
o=std::move(obj);
}

//useobj;
return0;
}

在上述代碼中,調用移動構造函數后,沒有將原對象回復默認值,導致目標對象和原對象的底層資源(data_)執行同一個內存塊,這樣就導致退出main()函數的時候,原對象和目標對象均調用析構函數釋放同一個內存塊,進而導致程序崩潰。

不要在函數中使用std::move()進行返回

我們仍然以Obj進行舉例,代碼如下:

Objfun(){
Objobj;
returnstd::move(obj);
}

intmain(){
Objo1=fun();
return0;
}

程序輸出:

inObj()0x7ffe600d79e0
inObj(constObj&&obj)
in~Obj()0x7ffe600d79e0

如果把fun()函數中的std::move(obj)換成return obj,則輸出如下:

inObj()0x7ffcfefaa750

通過上述示例的輸出,是不是有點超出我們的預期。從輸出可以看出來,第二種方式(直接return obj)比第一種方式少了一次move構造和析構。這是因為編譯器做了NRVO優化。

所以,我們需要切記:如果編譯器能夠對某個函數做(N)RVO優化,就使用(N)RVO,而不是自作聰明使用std::move()。

知己知彼

STL中大部分已經實現移動語義,比如std::vector<>,std::map<>等,同時std::unique_ptr<>等不能被拷貝的類也支持移動語義。

我們看下如下代碼:

classBigObj
{
public:
BigObj(){
std::cout<<__PRETTY_FUNCTION__<<std::endl;
}
~BigObj(){
std::cout<<__PRETTY_FUNCTION__<<std::endl;
}
BigObj(constBigObj&b){
std::cout<<__PRETTY_FUNCTION__<<std::endl;
}
BigObj(BigObj&&b){
std::cout<<__PRETTY_FUNCTION__<<std::endl;
}
};

intmain(){
std::arrayv;
autov1=std::move(v);

return0;
}

上述代碼輸出如下:

BigObj::BigObj()
BigObj::BigObj()
BigObj::BigObj(BigObj&&)
BigObj::BigObj(BigObj&&)
BigObj::~BigObj()
BigObj::~BigObj()
BigObj::~BigObj()
BigObj::~BigObj()

而如果把main()函數中的std::array<>換成std::vector<>后,如下:

intmain(){
std::vectorv;
v.resize(2);
autov1=std::move(v);

return0;
}

則輸出如下:

BigObj::BigObj()
BigObj::BigObj()
BigObj::~BigObj()
BigObj::~BigObj()

從上述兩處輸出可以看出,std::vector<>對應的移動構造不會生成多余的構造,且原本的element都移動到v1中;而相比std::array<>中對應的移動構造卻有很大的區別,基本上會對每個element都調用移動構造函數而不是對std::array<>本身。

因此,在使用std::move()的時候,最好要知道底層的基本實現原理,否則往往會得到我們意想不到的結果。

原文標題:【Modern C++】深入理解移動語義

文章出處:【微信公眾號:Linux愛好者】歡迎添加關注!文章轉載請注明出處。

審核編輯:湯梓紅
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • C++
    C++
    +關注

    關注

    22

    文章

    2114

    瀏覽量

    73795
  • 代碼
    +關注

    關注

    30

    文章

    4823

    瀏覽量

    68904
  • 語義
    +關注

    關注

    0

    文章

    21

    瀏覽量

    8675

原文標題:【Modern C++】深入理解移動語義

文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    詳解Linux C++內存管理

    在互聯網的服務C++常用于搭建高性能、高并發、大流量、低延時的后端服務。如何合理的分配內存滿足系統高性能需求是個高頻且重要的話題,而且因為內存自身的特點和實際問題的復雜,組合出了諸多難題。
    發表于 10-25 12:02 ?809次閱讀

    初識C++

    后接-個或多個字符組成的。后綴告訴系統這個文件是C++程序。不同編譯器使用不同的后綴命名約定,最常見的包括. cc、.cxx、.cpp、.cp及.C
    發表于 07-17 15:14 ?243次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>文</b>初識<b class='flag-5'>C++</b>

    詳解C/C++的getMemory()函數

    如果你將面試C/C++的工作,那么無論是筆試題或者面試題都有極大可能會被問到getMemory()的問題。當然這也是道比較糾結的題目,本文就對這幾道題目來做
    發表于 07-17 17:35 ?900次閱讀

    鴻蒙c++模板開發詳解

    鴻蒙c++模板開發詳解
    發表于 09-11 15:28

    C/C++軟件測試工具的元數據結構設計與實現

    針對用C/C++語言進行的語義分析,設計種中間結構,即元數據結構。元數據結構實現了源代碼的語義層次上的抽象,通過元數據結構和相關應用
    發表于 04-18 09:02 ?29次下載

    語義詳解

    語義詳解 1. 引言 2. 為什么需要語義網? 3. 
    發表于 08-04 10:33 ?2609次閱讀

    C++ 語言命令詳解(第二版)

    電子發燒友網站提供《C++ 語言命令詳解(第二版).txt》資料免費下載
    發表于 07-28 13:06 ?0次下載

    JAVA和C++區別詳解

    1)java是解釋性語言,java程序在運行時類加載器從類路經中加載相關的類,然后java虛擬機讀取該類文件的字節,執行相應操作.而C++編譯的 時候將程序編譯成本地機器碼.般來說java程序執行
    發表于 12-01 09:12 ?477次閱讀

    圖文詳解C++虛表的剖析

    圖文詳解C++虛表的剖析
    的頭像 發表于 06-29 14:23 ?2567次閱讀
    圖文<b class='flag-5'>詳解</b>:<b class='flag-5'>C++</b>虛表的剖析

    圖文詳解C++的輸出輸入

    圖文詳解C++的輸出輸入
    的頭像 發表于 06-29 14:53 ?3409次閱讀
    圖文<b class='flag-5'>詳解</b>:<b class='flag-5'>C++</b>的輸出輸入

    EE-128:C++的DSP:從C++調用匯編類成員函數

    EE-128:C++的DSP:從C++調用匯編類成員函數
    發表于 04-16 17:04 ?2次下載
    EE-128:<b class='flag-5'>C++</b><b class='flag-5'>中</b>的DSP:從<b class='flag-5'>C++</b>調用匯編類成員函數

    C++mutable關鍵字詳解與實戰

    mutable關鍵字詳解與實戰 在C++mutable關鍵字是為了突破const關鍵字的限制,被mutable關鍵字修飾的成員變量永遠處于可變的狀態,即使是在被const修飾的成員函數
    的頭像 發表于 09-10 09:23 ?5584次閱讀

    嵌入式編程C語言到C++詳解

    ? OOP第C語言的局限 C++的特點 C++的程序特征 C++程序的結構特性 C++程序
    的頭像 發表于 11-08 17:21 ?2622次閱讀

    詳解C/C++堆棧的工作機制

    參數,事實上是把參數壓入堆棧,聽起來,堆棧象個大雜燴。那么,堆棧(Stack)到底是如何工作的呢?本文將詳解C/C++堆棧的工作機制。閱讀時請注意以下幾點:
    的頭像 發表于 07-29 09:09 ?1176次閱讀

    C++移動語義介紹

    移動語義是從C++11開始引入的項全新功能。本文將為您撥開云霧,讓您對移動語義有個全面而深入的
    發表于 09-02 09:03 ?1844次閱讀
    主站蜘蛛池模板: 国产在线精彩亚洲 | 国产91网站在线观看免费 | 欧美xxxav| 一本道高清码v京东热 | 成人在线视频在线观看 | 猫咪最新破解版下载 | 美国色吧影院 | 国产剧情福利AV一区二区 | 成人高清护士在线播放 | 欧美色图一区二区三区 | 熟女久久久久久久久久久 | 最新国产在线视频 | 久久精品亚洲AV中文2区金莲 | 在线观看国产精选免费 | 强奷乱码中文字幕熟女免费 | 午夜神器老司机高清无码 | 国产成人在线播放 | 无码人妻99久久密AV | 国产精品永久在线 | 中文成人在线 | 动漫AV纯肉无码AV电影网 | 尤物国产在线精品三区 | yellow免费影视大全 | 麻豆精品一卡2卡三卡4卡免费观看 | 欲香欲色天天天综合和网 | 刺激一区仑乱 | 播色屋97超碰在人人 | 欧美中文字幕一区二区三区 | 日日噜噜夜夜狠狠视频 | 91九色视频无限观看免费 | 国产成人免费不卡在线观看 | 日韩欧美中文字幕在线 | 国产高清美女一级a毛片久久w | 欧美三级不卡在线观线看 | 亚洲国产在线播放在线 | 男人的天堂黄色片 | 国产高清免费观看 | 考试考90就可以晚上和老师C | 暖暖 日本 视频 在线观看免费 | 日本大片免a费观看视频 | 色色色久久久免费视频 |