真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

c++右值引用和移動(dòng)構(gòu)造實(shí)例分析

這篇文章主要介紹“c++右值引用和移動(dòng)構(gòu)造實(shí)例分析”的相關(guān)知識(shí),小編通過(guò)實(shí)際案例向大家展示操作過(guò)程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“c++右值引用和移動(dòng)構(gòu)造實(shí)例分析”文章能幫助大家解決問(wèn)題。

創(chuàng)新互聯(lián)建站主要從事成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)格爾木,10余年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來(lái)電咨詢建站服務(wù):18982081108

C++11 新特性分析
auto
 現(xiàn)在是一種類型占位符,它會(huì)告訴編譯器,應(yīng)該從初始化式中推斷出變量的實(shí)際類型。當(dāng)你想在不同的作用域中(例如,命名空間、函數(shù)內(nèi)、for循環(huán)中中的初始化式)聲明變量的時(shí)候,auto可以在這些場(chǎng)合使用。

auto i = 42;        // i is an int
auto l = 42LL;      // l is an long long
auto p = new foo(); // p is a foo*
 使用auto經(jīng)常意味著較少的代碼量(除非你需要的類型是int這種只有一個(gè)單詞的)。當(dāng)你想要遍歷STL容器中元素的時(shí)候,想一想你會(huì)怎么寫迭代器代碼,老式的方法是用很多typedef來(lái)做,而auto則會(huì)大大簡(jiǎn)化這個(gè)過(guò)程。

std::map> map;
for(auto it = begin(map); it != end(map); ++it) 
{
}
 你應(yīng)該注意到,auto并不能作為函數(shù)的返回類型,但是你能用auto去代替函數(shù)的返回類型,當(dāng)然,在這種情況下,函數(shù)必須有返回值才可以。auto不會(huì)告訴編譯器去推斷返回值的實(shí)際類型,它會(huì)通知編譯器在函數(shù)的末段去尋找返回值類型。在下面的那個(gè)例子中,函數(shù)返回值的構(gòu)成是由T1類型和T2類型的值,經(jīng)過(guò)+操作符之后決定的。

template
auto compose(T1 t1, T2 t2) -> decltype(t1 + t2)
{
   return t1+t2;
}
auto v = compose(2, 3.14); // v's type is double
nullptr關(guān)鍵字
 0曾經(jīng)是空指針的值,這種方式有一些弊端,因?yàn)樗梢员浑[式轉(zhuǎn)換成整型變量。nullptr關(guān)鍵字代表值類型std::nullptr_t,在語(yǔ)義上可以被理解為空指針。nullptr可被隱式轉(zhuǎn)換成任何類型的空指針,以及成員函數(shù)指針和成員變量指針,而且也可以轉(zhuǎn)換為bool(值為false),但是隱式轉(zhuǎn)換到整型變量的情況不再存在了。

void foo(int* p) {}
 
void bar(std::shared_ptr p) {}
 
int* p1 = NULL;
int* p2 = nullptr;   
if(p1 == p2)
{
}
 
foo(nullptr);
bar(nullptr);
 
bool f = nullptr;
int i = nullptr; // error: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type
 為了向下兼容,0仍可作為空指針的值來(lái)使用。

基于區(qū)間的循環(huán)
 C++11加強(qiáng)了for語(yǔ)句的功能,以更好的支持用于遍歷集合的foreach范式。在新的形式中,用戶可以使用for去迭代遍歷C風(fēng)格的數(shù)組、初始化列表,以及所有非成員begin()和end()被重載的容器。

 當(dāng)你僅僅想獲取集合/數(shù)組中的元素來(lái)做一些事情,而不關(guān)注索引值、迭代器或者元素本身的時(shí)候,這種for的形式非常有用。

std::map> map;
std::vector v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
map["one"] = v;
 
for(const auto& kvp : map) 
{
  std::cout << kvp.first << std::endl;
 
  for(auto v : kvp.second)
  {
     std::cout << v << std::endl;
  }
}
 
int arr[] = {1,2,3,4,5};
for(int& e : arr) 
{
  e = e*e;
}
Override和final
 虛函數(shù)在C++中會(huì)引起很多問(wèn)題,因?yàn)闆](méi)有一個(gè)強(qiáng)制的機(jī)制來(lái)標(biāo)識(shí)虛函數(shù)在派生類中被重寫了。virtual關(guān)鍵字并不是強(qiáng)制性的,這給代碼的閱讀增加了一些困難,因?yàn)槟憧赡懿坏貌蝗タ蠢^承關(guān)系的最頂層以確認(rèn)這個(gè)方法是不是虛方法。我自己經(jīng)常鼓勵(lì)開(kāi)發(fā)者在派生類中使用virtual關(guān)鍵字,我自己也是這么做的,這可以讓代碼更易讀。然而,有一些不明顯的錯(cuò)誤仍然會(huì)出現(xiàn),下面這段代碼就是個(gè)例子。

class B 
{
public:
   virtual void f(short) {std::cout << "B::f" << std::endl;}
};
 
class D : public B
{
public:
   virtual void f(int) {std::cout << "D::f" << std::endl;}
};
 D::f本應(yīng)該重寫B(tài)::f,但是這兩個(gè)函數(shù)的簽名并不相同,一個(gè)參數(shù)是short,另一個(gè)則是int,因此,B::f僅僅是另外一個(gè)和D::f命名相同的函數(shù),是重載而不是重寫。你有可能會(huì)通過(guò)B類型的指針調(diào)用f(),并且期盼輸出D::f的結(jié)果,但是打印出來(lái)的結(jié)果卻是B::f。

 這里還有另外一個(gè)不明顯的錯(cuò)誤:參數(shù)是相同的,但是在基類中的函數(shù)是const成員函數(shù),而在派生類中則不是。

class B 
{
public:
   virtual void f(int) const {std::cout << "B::f " << std::endl;}
};
 
class D : public B
{
public:
   virtual void f(int) {std::cout << "D::f" << std::endl;}
};
 又一次,這兩個(gè)函數(shù)的關(guān)系是重載而非重寫,因此,如果你想通過(guò)B類型的指針來(lái)調(diào)用f(),程序會(huì)打印出B::f,而不是D::f。

 幸運(yùn)的是,有一種方法可以來(lái)描述你的意圖,兩個(gè)新的、專門的標(biāo)識(shí)符(不是關(guān)鍵字)添加進(jìn)了C++11中:override,可以指定在基類中的虛函數(shù)應(yīng)該被重寫;final,可以用來(lái)指定派生類中的函數(shù)不會(huì)重寫基類中的虛函數(shù)。第一個(gè)例子會(huì)變成:

class B 
{
public:
   virtual void f(short) {std::cout << "B::f" << std::endl;}
};
 
class D : public B
{
public:
   virtual void f(int) override {std::cout << "D::f" << std::endl;}
};
 這段代碼會(huì)觸發(fā)一個(gè)編譯錯(cuò)誤(如果你使用override標(biāo)識(shí)符嘗試第二個(gè)例子,也會(huì)得到相同的錯(cuò)誤。):

 'D::f': 有override標(biāo)識(shí)符的函數(shù)并沒(méi)有重寫任何基類函數(shù)

 另一方面,如果你想要一個(gè)函數(shù)永遠(yuǎn)不能被重寫(順著繼承層次往下都不能被重寫),你可以把該函數(shù)標(biāo)識(shí)為final,在基類中和派生類中都可以這么做。如果實(shí)在派生類中,你可以同時(shí)使用override和final標(biāo)識(shí)符。

class B 
{
public:
   virtual void f(int) {std::cout << "B::f" << std::endl;}
};
 
class D : public B
{
public:
   virtual void f(int) override final {std::cout << "D::f" << std::endl;}
};
 
class F : public D
{
public:
   virtual void f(int) override {std::cout << "F::f" << std::endl;}
};
 用final聲明的函數(shù)不能被F::f重寫。

強(qiáng)類型枚舉
 “傳統(tǒng)”的C++枚舉類型有一些缺點(diǎn):它會(huì)在一個(gè)代碼區(qū)間中拋出枚舉類型成員(如果在相同的代碼域中的兩個(gè)枚舉類型具有相同名字的枚舉成員,這會(huì)導(dǎo)致命名沖突),它們會(huì)被隱式轉(zhuǎn)換為整型,并且不可以指定枚舉的底層數(shù)據(jù)類型。

 通過(guò)引入一種新的枚舉類型,這些問(wèn)題在C++11中被解決了,這種新的枚舉類型叫做強(qiáng)類型枚舉。這種類型用enum class關(guān)鍵字來(lái)標(biāo)識(shí),它永遠(yuǎn)不會(huì)在代碼域中拋出枚舉成員,也不會(huì)隱式的轉(zhuǎn)換為整形,同時(shí)還可以具有用戶指定的底層類型(這個(gè)特征也被加入了傳統(tǒng)枚舉類型中)。

enum class Options {None, One, All};
Options o = Options::All;
智能指針
 智能指針的引用計(jì)數(shù)和內(nèi)存自動(dòng)釋放相關(guān):

 unique_ptr:當(dāng)一塊內(nèi)存的所有權(quán)并不是共享的時(shí)候(它并不具有拷貝構(gòu)造函數(shù)),可以使用,但是,它可以被轉(zhuǎn)換為另外一個(gè)unique_ptr(具有移動(dòng)構(gòu)造函數(shù))。

 shared_ptr:當(dāng)一塊內(nèi)存的所有權(quán)可以被共享的時(shí)候,可以使用(這就是為什么它叫這個(gè)名)。

 weak_ptr:具有一個(gè)shared_ptr管理的指向一個(gè)實(shí)體對(duì)象的引用,但是并沒(méi)有做任何引用計(jì)數(shù)的工作,它被用來(lái)打破循環(huán)引用關(guān)系(想象一個(gè)關(guān)系樹(shù),父節(jié)點(diǎn)擁有指向子節(jié)點(diǎn)的引用(shared_ptr),但是子節(jié)點(diǎn)也必須持有指向父節(jié)點(diǎn)的引用;如果第二個(gè)引用也是一個(gè)獨(dú)立的引用,一個(gè)循環(huán)就產(chǎn)生了,這會(huì)導(dǎo)致任何對(duì)象都永遠(yuǎn)無(wú)法釋放)。
 換句話說(shuō),auto_ptr已經(jīng)過(guò)時(shí)了,應(yīng)該不再被使用了。

 什么時(shí)候該使用unique_ptr,什么時(shí)候該使用shared_ptr,取決于程序?qū)?nèi)存所有權(quán)的需求,我推薦你讀一讀這里的討論。

 下面第一個(gè)例子演示了unique_ptr的用法,如果你想要把對(duì)象的控制權(quán)轉(zhuǎn)交給另一個(gè)unique_ptr,請(qǐng)使用std::move。在控制權(quán)交接后,讓出控制權(quán)的智能指針會(huì)變成null,如果調(diào)用get(),會(huì)返回nullptr。

void foo(int* p)
{
   std::cout << *p << std::endl;
}
std::unique_ptr p1(new int(42));
std::unique_ptr p2 = std::move(p1); // transfer ownership
 
if(p1)
  foo(p1.get());
 
(*p2)++;
 
if(p2)
  foo(p2.get());
 第二個(gè)例子演示了shared_ptr的用法。盡管語(yǔ)義不同,因?yàn)樗袡?quán)是共享的,但用法都差不多。

void foo(int* p)
{
}
void bar(std::shared_ptr p)
{
   ++(*p);
}
std::shared_ptr p1(new int(42));
std::shared_ptr p2 = p1;
 
bar(p1);   
foo(p2.get());
 第一個(gè)聲明等價(jià)于這個(gè)。

auto p3 = std::make_shared(42);
 make_shared是一個(gè)非成員函數(shù),具有給共享對(duì)象分配內(nèi)存,并且只分配一次內(nèi)存的優(yōu)點(diǎn),和顯式通過(guò)構(gòu)造函數(shù)初始化的shared_ptr相比較,后者需要至少兩次分配內(nèi)存。這些額外的開(kāi)銷有可能會(huì)導(dǎo)致內(nèi)存溢出的問(wèn)題,在下一個(gè)例子中,如果seed()拋出一個(gè)異常,則表示發(fā)生了內(nèi)存溢出。

void foo(std::shared_ptr p, int init)
{
   *p = init;
}
foo(std::shared_ptr(new int(42)), seed());
 如果使用make_shared,則可以避開(kāi)類似問(wèn)題。第三個(gè)例子展示了weak_ptr的用法,注意,你必須通過(guò)調(diào)用lock()來(lái)獲取shared_ptr中指向?qū)ο蟮囊?,以此?lái)訪問(wèn)對(duì)象。

auto p = std::make_shared(42);
std::weak_ptr wp = p;
 
{
  auto sp = wp.lock();
  std::cout << *sp << std::endl;
}
 
p.reset();
 
if(wp.expired())
  std::cout << "expired" << std::endl;
 如果你試圖在一個(gè)已經(jīng)過(guò)期的weak_ptr上調(diào)用lock(被弱引用的對(duì)象已經(jīng)被釋放了),你會(huì)得到一個(gè)空的shared_ptr。

Lambdas表達(dá)式
 匿名的方法,也叫做lambda表達(dá)式,被加進(jìn)了C++11標(biāo)準(zhǔn)里,并且立刻得到了開(kāi)發(fā)者們的重視。這是一個(gè)從函數(shù)式語(yǔ)言中借鑒來(lái)的,非常強(qiáng)大的特征,它讓一些其他的特征和強(qiáng)大的庫(kù)得以實(shí)現(xiàn)。在任何函數(shù)對(duì)象、函數(shù)、std::function中出現(xiàn)的地方,你都可以用lambda表達(dá)式:

std::vector v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
 
std::for_each(std::begin(v), std::end(v), [](int n) {std::cout << n << std::endl;});
 
auto is_odd = [](int n) {return n%2==1;};
auto pos = std::find_if(std::begin(v), std::end(v), is_odd);
if(pos != std::end(v))
  std::cout << *pos << std::endl;
 有一點(diǎn)復(fù)雜的是遞歸lambda表達(dá)式。想象一個(gè)代表斐波那契函數(shù)的lambda表達(dá)式,如果你試圖用auto來(lái)寫這個(gè)函數(shù),你會(huì)得到編譯錯(cuò)誤:

auto fib = [&fib](int n) {return n < 2 ? 1 : fib(n-1) + fib(n-2);};
error C3533: 'auto &': a parameter cannot have a type that contains 'auto'
error C3531: 'fib': a symbol whose type contains 'auto' must have an initializer
error C3536: 'fib': cannot be used before it is initialized
error C2064: term does not evaluate to a function taking 1 arguments
 這個(gè)問(wèn)題是由于auto會(huì)根據(jù)初始化式來(lái)推斷對(duì)象類型,而初始化式卻包含了一個(gè)引用自己的表達(dá)式,因此,仍然需要知道它的類型,這是一個(gè)循環(huán)問(wèn)題。為了解決這個(gè)問(wèn)題,必須打破這個(gè)無(wú)限循環(huán),顯式的用std::function來(lái)指定函數(shù)類型。

std::function lfib = [&lfib](int n) {return n < 2 ? 1 : lfib(n-1) + lfib(n-2);};
非成員begin()和end()
 你也許已經(jīng)注意到了,我在上面的例子中已經(jīng)使用了非成員begin()和end()函數(shù),這些是新加到STL中的東西,提升了語(yǔ)言的標(biāo)準(zhǔn)性和一致性,也使更多的泛型編程變成了可能,它們和所有的STL容器都是兼容的,但卻不僅僅是簡(jiǎn)單的重載,因此你可以隨意擴(kuò)展begin()和end(),以便兼容任何類型,針對(duì)C類型數(shù)組的重載也一樣是支持的。

 讓我們舉一個(gè)前面寫過(guò)的例子,在這個(gè)例子中,我試圖打印輸出一個(gè)vector,并且找到它的第一個(gè)奇數(shù)值的元素。如果std::vector用C風(fēng)格數(shù)組來(lái)代替的話,代碼可能會(huì)像如下這樣:

int arr[] = {1,2,3};
std::for_each(&arr[0], &arr[0]+sizeof(arr)/sizeof(arr[0]), [](int n) {std::cout << n << std::endl;});
 
auto is_odd = [](int n) {return n%2==1;};
auto begin = &arr[0];
auto end = &arr[0]+sizeof(arr)/sizeof(arr[0]);
auto pos = std::find_if(begin, end, is_odd);
if(pos != end)
  std::cout << *pos << std::endl;
 如果你使用非成員begin()和end(),代碼可以這樣寫:

int arr[] = {1,2,3};
std::for_each(std::begin(arr), std::end(arr), [](int n) {std::cout << n << std::endl;});
 
auto is_odd = [](int n) {return n%2==1;};
auto pos = std::find_if(std::begin(arr), std::end(arr), is_odd);
if(pos != std::end(arr))
  std::cout << *pos << std::endl;
 這段代碼基本上和使用std::vector那段代碼一樣,這意味著我們可以為所有支持begin()和end()的類型寫一個(gè)泛型函數(shù)來(lái)達(dá)到這個(gè)目的。

template
void bar(Iterator begin, Iterator end) 
{
   std::for_each(begin, end, [](int n) {std::cout << n << std::endl;});
 
   auto is_odd = [](int n) {return n%2==1;};
   auto pos = std::find_if(begin, end, is_odd);
   if(pos != end)
      std::cout << *pos << std::endl;
}
 
template
void foo(C c)
{
   bar(std::begin(c), std::end(c));
}
 
template
void foo(T(&arr)[N])
{
   bar(std::begin(arr), std::end(arr));
}
 
int arr[] = {1,2,3};
foo(arr);
 
std::vector v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
foo(v);
static_assert宏和類型萃取器
 static_assert會(huì)執(zhí)行一個(gè)編譯器的的斷言,如果斷言為真,什么都不會(huì)發(fā)生,如果斷言為假,編譯器則會(huì)顯示一些特定的錯(cuò)誤信息。

template
class Vector
{
   static_assert(Size < 3, "Size is too small");
   T _points[Size];
};
 
int main()
{
   Vector a1;
   Vector a2;
   return 0;
}
error C2338: Size is too small
see reference to class template instantiation 'Vector' being compiled
   with
   [
      T=double,
      Size=2
   ]
 當(dāng)和類型萃取一起使用的時(shí)候,static_assert會(huì)變得更加有用,這些是一系列可以在編譯期提供額外信息的類,它們被封裝在了頭文件里面,在這個(gè)頭文件里,有若干分類:用來(lái)創(chuàng)建編譯期常量的helper類,用來(lái)編譯期獲取類型信息的類型萃取類,為了可以把現(xiàn)存類型轉(zhuǎn)換為新類型的類型轉(zhuǎn)換類。

 在下面那個(gè)例子里,add函數(shù)被設(shè)計(jì)成只能處理基本類型。

template
auto add(T1 t1, T2 t2) -> decltype(t1 + t2)
{
   return t1 + t2;
}
 然而,如果你這么寫的話,并不會(huì)出現(xiàn)編譯錯(cuò)誤。

std::cout << add(1, 3.14) << std::endl;
std::cout << add("one", 2) << std::endl;
 程序?qū)嶋H打印了4.14和“e”,但是如果我們添加一些編譯器斷言,這兩行代碼都會(huì)產(chǎn)生編譯錯(cuò)誤。

template
auto add(T1 t1, T2 t2) -> decltype(t1 + t2)
{
   static_assert(std::is_integral::value, "Type T1 must be integral");
   static_assert(std::is_integral::value, "Type T2 must be integral");
 
   return t1 + t2;
}
error C2338: Type T2 must be integral
see reference to function template instantiation 'T2 add(T1,T2)' being compiled
   with
   [
      T2=double,
      T1=int
   ]
error C2338: Type T1 must be integral
see reference to function template instantiation 'T1 add(T1,T2)' being compiled
   with
   [
      T1=const char *,
      T2=int
   ]
移動(dòng)語(yǔ)義
 這又是一個(gè)很重要,并且涉及到很多C++11技術(shù)特征的話題,關(guān)于這個(gè)話題不僅僅能寫一段,更能寫一系列文章。

 為了區(qū)分指向左值的引用和指向右值的引用,C++11引入了右值引用(用&&來(lái)表示)的概念。左值是指一個(gè)有名字的對(duì)象,而右值則是一個(gè)沒(méi)有名字的對(duì)象(臨時(shí)對(duì)象)。移動(dòng)語(yǔ)義允許修改右值(之前考慮到它的不可改變性,因此和const T& types的概念有些混淆)。

 一個(gè)C++類/結(jié)構(gòu)體有一些隱式成員函數(shù):默認(rèn)構(gòu)造函數(shù)(當(dāng)且僅當(dāng)另外一個(gè)構(gòu)造函數(shù)沒(méi)有被顯式的定義),拷貝構(gòu)造函數(shù),一個(gè)析構(gòu)函數(shù),以及一個(gè)拷貝賦值操作符??截悩?gòu)造函數(shù)和拷貝賦值操作符一般會(huì)執(zhí)行按位拷貝(或者淺拷貝),例如,逐一按位拷貝變量。這意味著如果你有一個(gè)包含指向某個(gè)對(duì)象的指針的類,它們只會(huì)把指針的地址進(jìn)行拷貝,并不會(huì)拷貝指針指向的對(duì)象。這在某些情況下是可以的,但是對(duì)于絕大多數(shù)情況,你需要的是深拷貝,也就是對(duì)指針指向的對(duì)象進(jìn)行拷貝,而不是指針本身的值,在這種情況下你不得不顯式的寫一個(gè)拷貝構(gòu)造函數(shù)和拷貝賦值操作符來(lái)執(zhí)行深拷貝。

 那么,如果你想要初始化或者復(fù)制的源數(shù)據(jù)是個(gè)右值類型(臨時(shí)的)會(huì)怎么樣?你仍然不得不拷貝它的值,但是很快,這個(gè)右值就會(huì)消失,這意味著一些操作的開(kāi)銷,包括分配內(nèi)存以及最后拷貝數(shù)據(jù),這些都是不必要的。

 我們引入了移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值操作符,這兩個(gè)特殊的函數(shù)接受一個(gè)T&&類型的右值參數(shù),這兩個(gè)函數(shù)可以修改對(duì)象,類似于把引用指向的對(duì)象“偷”來(lái)。舉一個(gè)例子,一個(gè)容器的具體實(shí)現(xiàn)(例如vector或者queue)可能會(huì)包含一個(gè)指向數(shù)組元素的指針,我們可以為這些元素分配另一個(gè)數(shù)組空間,從臨時(shí)空間中拷貝數(shù)據(jù),然后當(dāng)臨時(shí)數(shù)據(jù)失效的時(shí)候再刪除這段內(nèi)存,我們也可以直接用這個(gè)臨時(shí)的數(shù)據(jù)來(lái)實(shí)例化,我們只是拷貝指向數(shù)組元素的指針地址,于是,這節(jié)省了一次分配內(nèi)存的開(kāi)銷,拷貝一系列元素并且稍后釋放掉的開(kāi)銷。

 下面這個(gè)例子展示了一個(gè)虛擬緩沖區(qū)的實(shí)現(xiàn),這段緩沖區(qū)由一個(gè)名字標(biāo)識(shí)(只是為了能更好的解釋),有一個(gè)指針(用std::unique_ptr封裝起來(lái)),指向一個(gè)類型為T的數(shù)組,也有一個(gè)存儲(chǔ)數(shù)組大小的變量。

template
class Buffer 
{
   std::string          _name;
   size_t               _size;
   std::unique_ptr _buffer;
 
public:
   // default constructor
   Buffer():
      _size(16),
      _buffer(new T[16])
   {}
 
   // constructor
   Buffer(const std::string& name, size_t size):
      _name(name),
      _size(size),
      _buffer(new T[size])
   {}
 
   // copy constructor
   Buffer(const Buffer& copy):
      _name(copy._name),
      _size(copy._size),
      _buffer(new T[copy._size])
   {
      T* source = copy._buffer.get();
      T* dest = _buffer.get();
      std::copy(source, source + copy._size, dest);
   }
 
   // copy assignment operator
   Buffer& operator=(const Buffer& copy)
   {
      if(this != ?)
      {
         _name = copy._name;
 
         if(_size != copy._size)
         {
            _buffer = nullptr;
            _size = copy._size;
            _buffer = _size > 0 > new T[_size] : nullptr;
         }
 
         T* source = copy._buffer.get();
         T* dest = _buffer.get();
         std::copy(source, source + copy._size, dest);
      }
 
      return *this;
   }
 
   // move constructor
   Buffer(Buffer&& temp):
      _name(std::move(temp._name)),
      _size(temp._size),
      _buffer(std::move(temp._buffer))
   {
      temp._buffer = nullptr;
      temp._size = 0;
   }
 
   // move assignment operator
   Buffer& operator=(Buffer&& temp)
   {
      assert(this != &temp); // assert if this is not a temporary
 
      _buffer = nullptr;
      _size = temp._size;
      _buffer = std::move(temp._buffer);
 
      _name = std::move(temp._name);
 
      temp._buffer = nullptr;
      temp._size = 0;
 
      return *this;
   }
};
 
template
Buffer getBuffer(const std::string& name) 
{
   Buffer b(name, 128);
   return b;
}
int main()
{
   Buffer b1;
   Buffer b2("buf2", 64);
   Buffer b3 = b2;
   Buffer b4 = getBuffer("buf4");
   b1 = getBuffer("buf5");
   return 0;
}
 默認(rèn)拷貝構(gòu)造函數(shù)和復(fù)制賦值操作符應(yīng)該看起來(lái)很類似,對(duì)于C++11標(biāo)準(zhǔn)來(lái)說(shuō),新的東西是根據(jù)移動(dòng)語(yǔ)義設(shè)計(jì)的移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值操作符。如果你運(yùn)行這段代碼,你會(huì)看到,當(dāng)b4被構(gòu)造的時(shí)候,調(diào)用了移動(dòng)構(gòu)造函數(shù)。而當(dāng)b1被分配一個(gè)值的時(shí)候,移動(dòng)賦值操作符被調(diào)用了,原因則是getBuffer()返回的值是一個(gè)臨時(shí)的右值。

 你可能注意到了一個(gè)細(xì)節(jié),當(dāng)初始化name變量和指向buffer的指針的時(shí)候,我們?cè)谝苿?dòng)構(gòu)造函數(shù)中使用了std::move。name變量是一個(gè)字符串類型,std::string支持移動(dòng)語(yǔ)義,unique_ptr也是一樣的,然而,如果我們使用_name(temp._name),復(fù)制構(gòu)造函數(shù)將會(huì)被調(diào)用,但對(duì)于_buffer來(lái)說(shuō),這卻是不可能的,因?yàn)閟td::unique_ptr并沒(méi)有拷貝構(gòu)造函數(shù),但是為什么std::string的移動(dòng)構(gòu)造函數(shù)在這種情況下沒(méi)有被調(diào)用?因?yàn)榧词篂锽uffer調(diào)用移動(dòng)構(gòu)造函數(shù)的對(duì)象是一個(gè)右值類型,在構(gòu)造函數(shù)的內(nèi)部卻實(shí)際是個(gè)左值類型,為什么?因?yàn)樗幸粋€(gè)名字temp,而一個(gè)有名字的對(duì)象是左值類型。為了讓它再一次變成右值類型(也為了可以恰當(dāng)?shù)恼{(diào)用移動(dòng)構(gòu)造函數(shù)),我們必須使用std::move。這個(gè)函數(shù)的作用只是把一個(gè)左值類型的引用轉(zhuǎn)換成右值類型引用。

 更新:雖然這個(gè)例子的目的是展示下如何實(shí)現(xiàn)移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值操作符,但實(shí)現(xiàn)的具體細(xì)節(jié)可能會(huì)有所不同.

template
class Buffer
{
   std::string          _name;
   size_t               _size;
   std::unique_ptr _buffer;
 
public:
   // constructor
   Buffer(const std::string& name = "", size_t size = 16):
      _name(name),
      _size(size),
      _buffer(size? new T[size] : nullptr)
   {}
 
   // copy constructor
   Buffer(const Buffer& copy):
      _name(copy._name),
      _size(copy._size),
      _buffer(copy._size? new T[copy._size] : nullptr)
   {
      T* source = copy._buffer.get();
      T* dest = _buffer.get();
      std::copy(source, source + copy._size, dest);
   }
 
   // copy assignment operator
   Buffer& operator=(Buffer copy)
   {
       swap(*this, copy);
       return *this;
   }
 
   // move constructor
   Buffer(Buffer&& temp):Buffer()
   {
      swap(*this, temp);
   }
 
   friend void swap(Buffer& first, Buffer& second) noexcept
   {
       using std::swap;
       swap(first._name  , second._name);
       swap(first._size  , second._size);
       swap(first._buffer, second._buffer);
   }
};

關(guān)于“c++右值引用和移動(dòng)構(gòu)造實(shí)例分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。


當(dāng)前標(biāo)題:c++右值引用和移動(dòng)構(gòu)造實(shí)例分析
URL網(wǎng)址:http://weahome.cn/article/pchihs.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部