日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

2.5w字长文爆肝 C++动态内存与智能指针一篇搞懂!太顶了!!!

發(fā)布時(shí)間:2023/12/4 c/c++ 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 2.5w字长文爆肝 C++动态内存与智能指针一篇搞懂!太顶了!!! 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

動(dòng)態(tài)內(nèi)存與智能指針

  • 1.動(dòng)態(tài)內(nèi)存與智能指針
  • 2.shared_ptr類
    • 2.1.make_shared函數(shù)
    • 2.2.shared_ptr的拷貝和賦值
    • 2.3.shared_ptr自動(dòng)銷毀所管理的對象
    • 2.4. shared_ptr會(huì)自動(dòng)釋放相關(guān)聯(lián)的內(nèi)存
    • 2.5.使用了動(dòng)態(tài)生存期的資源的類
    • 2.6.定義StrBlob類
    • 2.7. StrBlob構(gòu)造函數(shù)
    • 2.8.元素訪問成員函數(shù)
    • 2.9.StrBlob的拷貝,賦值和銷毀
    • 2.9.5StrBlob類的測試
  • 3.直接管理內(nèi)存
    • 3.1.使用new動(dòng)態(tài)分配和初始化對象
    • 3.2.動(dòng)態(tài)分配的const對象
    • 3.3.釋放動(dòng)態(tài)內(nèi)存
    • 3.4.指針指和delete
    • 3.5.動(dòng)態(tài)對象的生存期直到被釋放時(shí)為止
    • 3.6.小心:動(dòng)態(tài)內(nèi)存的管理非常容易出錯(cuò)
    • 3.7.delete之后重置指針值,這只是提供了有限的保護(hù)
  • 4.shared_ptr和new結(jié)合使用
    • 4.1.不要混合使用普通指針和智能指針
    • 4.2.不要使用get初始化另一個(gè)智能指針或?yàn)橹悄苤羔樫x值
    • 4.3.shared_ptr指針操作:reset
    • 4.4.shared_ptr的刪除器
  • 5.智能指針和異常
    • 5.1注意:智能指針陷阱
  • 6.unique_ptr
    • 6.1.傳遞unique_ptr參數(shù)和返回
    • 6.2.unique_ptr的刪除器
  • 7.weak_ptr
    • 7.1.核查指針類
    • 7.2.指針操作
    • 7.3.StrBlobPtr類的測試
    • 7.4.小插曲:改進(jìn)StrBlobPtr的構(gòu)造函數(shù)

本篇參考于《C++Primer》

三種內(nèi)存區(qū)別:

  • 靜態(tài)存儲(chǔ)區(qū)
    主要存放static靜態(tài)變量、全局變量、常量。這些數(shù)據(jù)內(nèi)存在編譯的時(shí)候就已經(jīng)為他們分配好了內(nèi)存,生命周期是整個(gè)程序從運(yùn)行到結(jié)束。
  • 棧區(qū)
    存放局部變量。在執(zhí)行函數(shù)的時(shí)候(包括main這樣的函數(shù)),函數(shù)內(nèi)的局部變量的存儲(chǔ)單元會(huì)在棧上創(chuàng)建,函數(shù)執(zhí)行完自動(dòng)釋放,生命周期是從該函數(shù)的開始執(zhí)行到結(jié)束。線性結(jié)構(gòu)。
  • 堆區(qū)(存儲(chǔ)動(dòng)態(tài)分配的對象)
    程序員自己申請的任意大小的內(nèi)存。一直存在直到被釋放。鏈表結(jié)構(gòu)。
  • 1.動(dòng)態(tài)內(nèi)存與智能指針

    在C++中,動(dòng)態(tài)內(nèi)存的管理是通過一對運(yùn)算符來完成的。

  • new,在動(dòng)態(tài)內(nèi)存中為對象分配空間并返回一個(gè)指向該對象的指針,我們可以選擇對對象進(jìn)行初始化;
  • delete,接受一個(gè)動(dòng)態(tài)對象的指針,銷毀該對象,并釋放與之關(guān)聯(lián)的內(nèi)存。
  • 動(dòng)態(tài)內(nèi)存的使用很容易出問題,因?yàn)榇_保在正確的時(shí)間釋放內(nèi)存是極其困難的。有時(shí)我們會(huì)忘記釋放內(nèi)存,在這種情況下就會(huì)產(chǎn)生內(nèi)存泄漏;有時(shí)在尚有指針引用內(nèi)存的情況下我們就釋放了它,在這種情況下就會(huì)產(chǎn)生引用非法內(nèi)存的指針。

    為了更容易(同時(shí)也更安全)地使用動(dòng)態(tài)內(nèi)存,新的標(biāo)準(zhǔn)庫提供了兩種智能指針(smart pointer)類型來管理動(dòng)態(tài)對象。智能指針的行為類似常規(guī)指針,重要的區(qū)別是它負(fù)責(zé)自動(dòng)釋放所指向的對象。新標(biāo)準(zhǔn)庫提供的這兩種智能指針的區(qū)別在于管理底層指針的方式:

  • shared_ptr允許多個(gè)指針指向同一個(gè)對象;
  • unique_ptr則“獨(dú)占”所指向的對象。
  • 標(biāo)準(zhǔn)庫還定義了一個(gè)名為weak_ptr的伴隨類,它是一種弱引用,指向shared_ptr所管理的對象。
  • 這三種類型都定義在memory頭文件中。

    2.shared_ptr類

    類似vector,智能指針也是模板,與vector一樣,我們在尖括號(hào)內(nèi)給出類型,之后是所定義的這種智能指針的名字:

    shared_ptr<string>p1;//shared_ptr,可以指向string shared_ptr<list<int>>p2;//shared_ptr,可以指向int的list

    默認(rèn)初始化的智能指針中保存著一個(gè)空指針,后面我們將介紹初始化智能指針的其他方法。

    智能指針的使用方式與普通指針類似。解引用一個(gè)智能指針返回它指向的對象。如果在一個(gè)條件判斷中使用智能指針,效果就是檢測它是否為空

    下面列出shared_ptr和unique_ptr所支持的操作。


    圖片來源于《C++Primer》

    2.1.make_shared函數(shù)

    最安全的分配和使用動(dòng)態(tài)內(nèi)存的方法是調(diào)用一個(gè)名為make_shared的標(biāo)準(zhǔn)庫函數(shù)。此函數(shù)在動(dòng)態(tài)內(nèi)存中分配一個(gè)對象并初始化它,返回指向此對象的shared_ptr。與智能指針一樣,make_shared也定義在頭文件memory中。
    當(dāng)要用make_shared時(shí),必須指定想要?jiǎng)?chuàng)建的對象的類型。定義方式與模板類相同,在函數(shù)名之后跟一個(gè)尖括號(hào),在其中給出類型:

    //p3指向一個(gè)值為42的int的shared_ptr shared_ptr<int>p3 = make_shared<int>(42); //p4指向一個(gè)值為“9999999999”的string shared_ptr<string>p4 = make_shared<string>(10,'9'); //p5指向一個(gè)值初始化的int,即值為0 shared_ptr<int>p5 = make_shared<int>();

    我們通常用auto定義一個(gè)對象來保存make_shared的結(jié)果,這種方式比較簡單:

    //p6指向一個(gè)動(dòng)態(tài)分配的空vector<string> auto p6 = make_shared<vector<string>>();

    2.2.shared_ptr的拷貝和賦值

    當(dāng)進(jìn)行拷貝或賦值操作時(shí),每個(gè)shared_ptr都會(huì)記錄有多少個(gè)其他shared_ptr指向相同的對象:

    auto p = make_shared<int>(42);//p指向的對象只有p一個(gè)引用者 auto q(p);//p和q指向相同對象,此對象有兩個(gè)引用者

    我們可以認(rèn)為每個(gè)shared_ptr都有一個(gè)關(guān)聯(lián)的計(jì)數(shù)器,通常稱其為引用計(jì)數(shù)。無論何時(shí)我們拷貝一個(gè)shared_ptr,計(jì)數(shù)器都會(huì)遞增。例如,當(dāng)用一個(gè)shared_ptr初始化另一個(gè)shared_ptr,或?qū)⑺鳛閰?shù)傳遞給一個(gè)函數(shù)以及作為函數(shù)的返回值時(shí),它所關(guān)聯(lián)的計(jì)數(shù)器就會(huì)遞增當(dāng)我們給shared_ptr賦予一個(gè)新值或是shared_ptr被銷毀(例如一個(gè)局部的shared_ptr離開其作用域時(shí),計(jì)數(shù)器就會(huì)遞減。
    一旦一個(gè)shared_ptr的計(jì)數(shù)器變?yōu)?,它就會(huì)自動(dòng)釋放自己所管理的對象:

    auto r = make_shared<int>(42);//r指向的int只有一個(gè)引用者 r = q;//給r賦值,令它指向另一個(gè)地址 //遞增q指向的對象的引用計(jì)數(shù) //遞減r原來指向的對象的引用計(jì)數(shù) //r原來指向的對象已沒有引用者,會(huì)自動(dòng)釋放

    此例中我們分配了一個(gè)int,將其指針保存在r中。接下來,我們將一個(gè)新值賦予r。在此情況下,r是唯一指向此int的shared_ptr,在把q賦給r的過程中,此int被自動(dòng)釋放。

    2.3.shared_ptr自動(dòng)銷毀所管理的對象

    當(dāng)指向一個(gè)對象的最后一個(gè)shared_ptr被銷毀時(shí),shared_ptr類會(huì)自動(dòng)銷毀此對象。它是通過另一個(gè)特殊的成員函數(shù)——析構(gòu)函數(shù)完成銷毀工作的。

    shared_ptr的析構(gòu)函數(shù)會(huì)遞減它所指向的對象的引用計(jì)數(shù)。如果引用計(jì)數(shù)變?yōu)?,shared_ptr的析構(gòu)函數(shù)就會(huì)銷毀對象,并釋放它占用的內(nèi)存。

    2.4. shared_ptr會(huì)自動(dòng)釋放相關(guān)聯(lián)的內(nèi)存

    當(dāng)動(dòng)態(tài)對象不再被使用時(shí),shared_ptr類會(huì)自動(dòng)釋放動(dòng)態(tài)對象,這一特性使得動(dòng)態(tài)內(nèi)存的使用變得非常容易。

    2.5.使用了動(dòng)態(tài)生存期的資源的類

    程序使用動(dòng)態(tài)內(nèi)存出于以下三種原因之一:
    1.程序不知道自己需要使用多少對象
    2.程序不知道所需對象的準(zhǔn)確類型
    3.程序需要在多個(gè)對象間共享數(shù)據(jù)

    容器類是出于第一種原因而使用動(dòng)態(tài)內(nèi)存的典型例子,這里我們拿vector容器舉例,每個(gè)vector“擁有”其自己的元素。當(dāng)我們拷貝一個(gè)vector時(shí),原vector和副本vector中的元素是相互分離的:

    vector<string>v1;//空vector {//新作用域vector<string>v2 = {"a","an","the"};v1 = v2;//從v2拷貝元素到v1中 }//v2被銷毀,其中的元素也被銷毀 //v1有三個(gè)元素,是原來v2中元素的拷貝

    由一個(gè)vector分配的元素只有當(dāng)這個(gè)vector存在時(shí)才存在。當(dāng)一個(gè)vector被銷毀時(shí),這個(gè)vector中的元素也都被銷毀。

    出于第二種原因而使用動(dòng)態(tài)內(nèi)存的情況我們在這里不介紹。

    我們現(xiàn)在來定義一個(gè)名為Blob的類,保存一組元素。與容器不同,我們希望Blob對象的不同拷貝之間共享相同的元素。即,當(dāng)我們拷貝一個(gè)Blob時(shí),原Blob對象及其拷貝應(yīng)該引用相同的底層元素。
    一般而言,如果兩個(gè)對象共享底層的數(shù)據(jù),當(dāng)某個(gè)對象被銷毀時(shí),我們不能單方面地銷毀底層數(shù)據(jù):

    Blob<string>b1;//空Blob {//新作用域Blob<string>b2 = {"a","an","the"};b1 = b2;//b1和b2共享相同的元素 }//b2被銷毀了,但b2中的元素不能被銷毀 //b1指向最初由b2創(chuàng)建的元素

    在此例中,b1和b2共享相同的元素。當(dāng)b2離開作用域時(shí),這些元素必須保留,因?yàn)閎1仍然在使用它們。

    note:
    使用動(dòng)態(tài)內(nèi)存的一個(gè)常見原因是允許多個(gè)對象共享相同的狀態(tài)。

    2.6.定義StrBlob類

    現(xiàn)在讓我們來編寫在多個(gè)對象間共享數(shù)據(jù)的類,在這里我們不用模板來實(shí)現(xiàn)(因?yàn)槲疫€不會(huì)模板),因此,現(xiàn)在我們先定義一個(gè)管理string的類,此版本命名為StrBlob。
    實(shí)現(xiàn)一個(gè)新的集合類型的最簡單方法是使用某個(gè)標(biāo)準(zhǔn)庫容器來管理元素。采用這種方法,我們可以借助標(biāo)準(zhǔn)庫類型來管理元素所使用的內(nèi)存空間。在本例中,我們將使用vector來保存元素。
    但是,我們不能在一個(gè)Blob對象內(nèi)直接保存vector,因?yàn)橐粋€(gè)對象的成員在對象銷毀時(shí)也會(huì)被銷毀。例如,假定b1和b2是兩個(gè)Blob對象,共享相同的vector。如果此vector保存在其中一個(gè)Blob中——例如b2中,那么當(dāng)b2離開作用域時(shí),此vector也將被銷毀,也就是說其中的元素都將不復(fù)存在。為了保證vector中的元素繼續(xù)存在,我們將vector保存在動(dòng)態(tài)內(nèi)存中。
    為了實(shí)現(xiàn)我們所希望的數(shù)據(jù)共享,我們?yōu)槊總€(gè)StrBlob設(shè)置一個(gè)shared_ptr來管理動(dòng)態(tài)分配的vector。此shared_ptr的成員將記錄有多少個(gè)StrBlob共享相同的vector,并在vector的最后一個(gè)使用者被銷毀時(shí)釋放vector。
    我們還需要確定這個(gè)類應(yīng)該提供什么操作。當(dāng)前,我們將實(shí)現(xiàn)一個(gè)vector操作的小的子集。我們會(huì)修改訪問元素的操作(如front和back):在我們的類中,如果用戶試圖訪問不存在的元素,這些操作會(huì)拋出一個(gè)異常。
    我們的類有一個(gè)默認(rèn)構(gòu)造函數(shù)和一個(gè)構(gòu)造函數(shù),接受單一的initializer_list類型參數(shù)。此構(gòu)造函數(shù)可以接受一個(gè)初始化器的花括號(hào)列表。

    代碼如下:

    class StrBlob{public:typedef std::vector<std::string>::size_type size_type;StrBlob();StrBlob(std::initializer_list<std::string>il);size_type size()const{return data->size();}bool empty()const {return data->empty();}void push_back(const std::string &t){data->push_back(t);}void pop_back();std::string&front();std::string&back();private:std::shared_ptr<std::vector<std::string>>data;void check(size_type i,const std::string &msg)const; };

    在此類中,我們實(shí)現(xiàn)了size、empty和push_back成員。這些成員通過指向底層vector的data成員來完成它們的工作。例如,對一個(gè)StrBlob對象調(diào)用size()會(huì)調(diào)用data->size(),依此類推。

    2.7. StrBlob構(gòu)造函數(shù)

    兩個(gè)構(gòu)造函數(shù)都使用初始化列表來初始化其data成員,令它指向一個(gè)動(dòng)態(tài)分配的vector。默認(rèn)構(gòu)造函數(shù)分配一個(gè)空vector:

    StrBlob::StrBlob():data(make_shared<vector<string>>()){} StrBlob::StrBlob(initializer_list<string>il):data(make_shared<vector<string>>(il)){}

    接受一個(gè)initializer_list的構(gòu)造函數(shù)將其參數(shù)傳遞給對應(yīng)的vector構(gòu)造函數(shù)。此構(gòu)造函數(shù)通過拷貝列表中的值來初始化vector的元素。

    2.8.元素訪問成員函數(shù)

    pop_back、front和back操作訪問vector中的元素。這些操作在試圖訪問元素之前必須檢查元素是否存在。由于這些成員函數(shù)需要做相同的檢查操作,我們?yōu)镾trBlob定義了一個(gè)名為check的private工具函數(shù),它檢查一個(gè)給定索引是否在合法范圍內(nèi)。除了索引,check還接受一個(gè)string參數(shù),它會(huì)將此參數(shù)傳遞給異常處理程序,這個(gè)string描述了錯(cuò)誤內(nèi)容:

    pop_back和元素訪問成員函數(shù)首先調(diào)用check。如果check成功,這些成員函數(shù)繼續(xù)利用底層vector的操作來完成自己的工作:

    void StrBlob::check(size_type i,const string &msg) const {if (i >= data->size())throw out_of_range(msg); }

    pop_back和元素訪問成員函數(shù)首先調(diào)用check。如果check成功,這些成員函數(shù)繼續(xù)利用底層vector的操作來完成自己的工作:

    string &StrBlob::front() {check(0,"front on empty StrBlob");return data->front(); }string &StrBlob::back() {check(0,"back on empty StrBlob");return data->back(); }void StrBlob::pop_back() {check(0,"pop_back on empty StrBlob");data->pop_back(); }

    front和back應(yīng)該對const進(jìn)行重載

    2.9.StrBlob的拷貝,賦值和銷毀

    StrBlob使用默認(rèn)版本的拷貝、賦值和銷毀成員函數(shù)來對此類型的對象進(jìn)行這些操作。默認(rèn)情況下,這些操作拷貝、賦值和銷毀類的數(shù)據(jù)成員。我們的StrBlob類只有一個(gè)數(shù)據(jù)成員,它是shared_ptr類型。因此,當(dāng)我們拷貝、賦值或銷毀一個(gè)StrBlob對象時(shí),它的shared_ptr成員會(huì)被拷貝、賦值或銷毀。
    如前所見,拷貝一個(gè)shared_ptr會(huì)遞增其引用計(jì)數(shù);將一個(gè)shared_ptr賦予另一個(gè)shared_ptr會(huì)遞增賦值號(hào)右側(cè)shared_ptr的引用計(jì)數(shù),而遞減左側(cè)shared_ptr的引用計(jì)數(shù)。如果一個(gè)shared_ptr的引用計(jì)數(shù)變?yōu)?,它所指向的對象會(huì)被自動(dòng)銷毀。因此,對于由StrBlob構(gòu)造函數(shù)分配的vector,當(dāng)最后一個(gè)指向它的StrBlob對象被銷毀時(shí),它會(huì)隨之被自動(dòng)銷毀。

    2.9.5StrBlob類的測試

    完整代碼如下:

    #ifndef MY_STRBLOB_H #define MY_STRBLOB_H #include <vector> #include <string> #include <initializer_list> #include <memory> #include <stdexcept>using namespace std;class StrBlob {public:typedef vector<string>::size_type size_type;StrBlob();StrBlob(initializer_list<string>il);size_type size()const {return data->size();}bool empty()const {return data->empty();}void push_back(const string &t) {data->push_back(t);}void pop_back();string &front();const string &front()const;string &back();const string &back()const;private:shared_ptr<std::vector<std::string>>data;void check(size_type i, const std::string &msg)const; };StrBlob::StrBlob(): data(make_shared<vector<string>>()) { }StrBlob::StrBlob(initializer_list<string>il): data(make_shared<vector<string>>(il)) {}void StrBlob::check(size_type i, const string &msg)const {if (i >= data->size())throw out_of_range(msg); }string &StrBlob::front() {check(0, "front on empty StrBlob");return data->front(); }const string &StrBlob::front()const {check(0, "front on empty StrBlob");return data->front(); }string &StrBlob::back() {check(0, "back on empty StrBlob");return data->back(); }const string &StrBlob::back()const {check(0, "back on empty StrBlob");return data->back(); }void StrBlob::pop_back() {check(0, "pop_back on empty StrBlob");data->pop_back(); }#endif

    測試代碼如下:

    #include <iostream> using namespace std; #include "my_StrBlob.h"int main() {StrBlob b1;StrBlob b2 = {"a", "an", "the"};b1 = b2;b2.push_back("about");cout << b2.size() << endl;cout << b1.size() << endl;cout << b1.front() << " " << b1.back() << endl;const StrBlob b3 = b1;cout << b3.front() << " " << b3.back() << endl;return 0; }

    測試結(jié)果:

    普通vector代碼如下:

    #include <iostream> #include <vector> using namespace std;int main() {vector<string>b1;vector<string> b2 = {"a", "an", "the"};b1 = b2;b2.push_back("about");cout << b2.size() << endl;cout << b1.size() << endl;cout << b1.front() << " " << b1.back() << endl;const vector<string> b3 = b1;cout << b3.front() << " " << b3.back() << endl;return 0; }

    測試結(jié)果:

    3.直接管理內(nèi)存

    C++定義了new分配內(nèi)存,delete釋放new分配的內(nèi)存。

    相對于智能指針,使用這兩個(gè)運(yùn)算符管理內(nèi)存非常容易出錯(cuò)

    而且,自己直接管理內(nèi)存的類與使用智能指針的類不同,它們不能依賴類對象拷貝、賦值和銷毀操作的任何默認(rèn)定義。因此,使用智能指針的程序更容易編寫和調(diào)試。

    3.1.使用new動(dòng)態(tài)分配和初始化對象

    在自由空間分配的內(nèi)存是無名的,因此new無法為其分配的對象命名,而是返回一個(gè)指向該對象的指針:

    int *pi = new int;//pi指向一個(gè)動(dòng)態(tài)分配的,未初始化的無名對象

    此new表達(dá)式在自由空間構(gòu)造一個(gè)int型對象,并返回指向該對象的指針。

    默認(rèn)情況下,動(dòng)態(tài)分配的對象是默認(rèn)初始化的,這意味著內(nèi)置類型或組合類型的對象的值將是未定義的,而類類型對象將用默認(rèn)構(gòu)造函數(shù)進(jìn)行初始化

    string *ps = new string;//初始化空string int *pi = new int;//pi指向一個(gè)未初始化的int

    初始化方式(1.使用圓括號(hào),2.使用列表初始化,3.在類型名之后跟一對空括號(hào)進(jìn)行值初始化):

    int *pi = new int(1024);//pi指向的對象的值為1024 string *ps = new string(10,'9');//*ps為"9999999999" vector<int>*pv = new vector<int>{0,1,2,3,4,5,6,7,8,9}; //vector有10個(gè)元素,值依次從0到9string *ps1 = new string;//默認(rèn)初始化為空string string *ps = new string();//值初始化為空string int *pi1 = new int;//默認(rèn)初始化;*pi1的值未定義 int *pi2 = new int();//值初始化為0,*pi2為0

    如果我們用auto來初始化,由于編譯器要用初始化器的類型來推斷要分配的類型,只有當(dāng)括號(hào)中僅有單一初始化器時(shí)才可以使用auto:

    auto p1 = new auto(obj);//p指向一個(gè)與obj類型相同的對象//該對象用obj進(jìn)行初始化 auto p2 = new auto{a,b,c};//錯(cuò)誤;括號(hào)中只能有單個(gè)初始化器

    p1的類型是一個(gè)指針,指向從obj自動(dòng)推斷出的類型。若obj是一個(gè)int,那么p1就是int*;若obj是一個(gè)string,那么p1是一個(gè)string*;依此類推。新分配的對象用obj的值進(jìn)行初始化。

    3.2.動(dòng)態(tài)分配的const對象

    用new分配const對象是合法的:

    const int *pci = new const int(1024); //分配并初始化一個(gè)const int const string *pcs = new const string; //分配并默認(rèn)初始化一個(gè)const的空string

    類似其他任何const對象,一個(gè)動(dòng)態(tài)分配的const對象必須進(jìn)行初始化。對于一個(gè)定義了默認(rèn)構(gòu)造函數(shù)的類類型,其const動(dòng)態(tài)對象可以隱式初始化,而其他類型的對象就必須顯式初始化。由于分配的對象是const的,new返回的指針是一個(gè)指向const的指針。

    3.3.釋放動(dòng)態(tài)內(nèi)存

    為了防止內(nèi)存耗盡(在這里不介紹),在動(dòng)態(tài)內(nèi)存使用完畢后,必須將其歸還給系統(tǒng)。我們通過delete表達(dá)式來將動(dòng)態(tài)內(nèi)存歸還給系統(tǒng)。delete表達(dá)式接受一個(gè)指針,指向我們想要釋放的對象:

    delete p;//p必須指向一個(gè)動(dòng)態(tài)分配的對象或是一個(gè)空指針

    note:
    delete p; 之后,p的值(指向的地址不變),但不能再使用p處理該地址的內(nèi)容,也不能再delete p,因?yàn)閐elete之后,p不指向nullptr。
    通常需要delete之后置指針為空:
    delete p;
    p = nullptr;


    與new類型類似,delete表達(dá)式也執(zhí)行兩個(gè)動(dòng)作:銷毀給定的指針指向的對象;釋放對應(yīng)的內(nèi)存。

    3.4.指針指和delete

    我們傳遞給delete的指針必須指向動(dòng)態(tài)分配的內(nèi)存,或者是一個(gè)空指針。釋放一塊并非new分配的內(nèi)存,或者將相同的指針值釋放多次,又或者釋放一個(gè)局部變量等,其行為是未定義的。

    舉個(gè)例子:
    如果我們delete了局部變量,那么當(dāng)局部變量離開了它的作用域,系統(tǒng)要銷毀這個(gè)局部變量!可這個(gè)局部變量已經(jīng)被我們銷毀了,就相當(dāng)于將相同的指針值釋放多次。(如果錯(cuò)誤,還請指正)

    雖然一個(gè)const對象的值不能被改變,但它本身是可以被銷毀的。

    const int *pci = new const int(1024); delete pci;

    3.5.動(dòng)態(tài)對象的生存期直到被釋放時(shí)為止

    注意:調(diào)用者必須記得釋放內(nèi)存

    讓我們看下面這段代碼:

    #include <iostream> using namespace std;int *fff(int a) //返回一個(gè)指針,指向一個(gè)動(dòng)態(tài)分配的對象 {return new int(a); }void use_fff(int a) {int *b = fff(a);//調(diào)用者必須記得釋放此內(nèi)存cout << *b << endl; }//b離開了它的作用域,但它所指向的內(nèi)存沒有被釋放!!!int main() {use_fff(42);return 0; }

    注意:
    由內(nèi)置指針(而不是智能指針)管理的動(dòng)態(tài)內(nèi)存在被顯式釋放前一直都會(huì)存在。

    #include <iostream> using namespace std;int *fff(int a) { //返回一個(gè)指針,指向一個(gè)動(dòng)態(tài)分配的對象return new int(a); }void use_fff(int a) {int *b = fff(a);//調(diào)用者必須記得釋放此內(nèi)存cout << *b << endl;delete b;//現(xiàn)在記得釋放內(nèi)存,我們已經(jīng)不需要它了 }int main() {use_fff(42);return 0; }

    當(dāng)然,如果在這段代碼中,我們不釋放內(nèi)存,那么目的就是,我們的系統(tǒng)中的其他代碼要使用use_fff所分配的對象,我們就應(yīng)該修改此函數(shù),讓它返回一個(gè)指針,指向它分配的內(nèi)存:

    #include <iostream> using namespace std;int *fff(int a) { //返回一個(gè)指針,指向一個(gè)動(dòng)態(tài)分配的對象return new int(a); }int *use_fff(int a) {int *b = fff(a);//調(diào)用者必須記得釋放此內(nèi)存cout << *b << endl;return b; }int main() {int *c = use_fff(42);cout << *c << endl;return 0; }

    3.6.小心:動(dòng)態(tài)內(nèi)存的管理非常容易出錯(cuò)

  • 忘記delete內(nèi)存
  • 使用已釋放掉的內(nèi)存
  • 同一塊內(nèi)存釋放兩次
  • 堅(jiān)持只使用智能指針,就可以避免所有這些問題。對于一塊內(nèi)存,只有在沒有任何智能指針指向它的情況下,智能指針才會(huì)自動(dòng)釋放它。

    3.7.delete之后重置指針值,這只是提供了有限的保護(hù)

    當(dāng)我們delete一個(gè)指針后,指針值就變?yōu)闊o效了。雖然指針無效,但是很多機(jī)器上指針(已經(jīng)釋放了的)仍然保存著動(dòng)態(tài)內(nèi)存的地址。在delete之后,指針就變成了空懸指針

    空懸指針:指向一塊曾經(jīng)保存數(shù)據(jù)對象但現(xiàn)在已經(jīng)無效的內(nèi)存的指針。

    未初始化指針的所有缺點(diǎn)空懸指針也都有。有一種方法可以避免空懸指針的問題:在指針即將要離開其作用域之前釋放掉它所關(guān)聯(lián)的內(nèi)存。這樣,在指針關(guān)聯(lián)的內(nèi)存被釋放掉之后,就沒有機(jī)會(huì)繼續(xù)使用指針了。如果我們需要保留指針,可以在delete之后將nullptr賦予指針,這樣就清楚地指出指針不指向任何對象。

    但這也僅僅提供了有限的保護(hù)。

    比如下面這個(gè)例子:

    int *p(new int(42));//p指向動(dòng)態(tài)內(nèi)存 auto q = p;//p和q指向相同內(nèi)存 delete p;//p和q均變?yōu)闊o效 p = nullptr;//指出p不再綁定到任何對象

    本例中p和q指向相同的動(dòng)態(tài)分配的對象。我們delete此內(nèi)存,然后將p置為nullptr,指出它不再指向任何對象。但是,重置p對q沒有任何作用,在我們釋放p所指向的(同時(shí)也是q所指向的!)內(nèi)存時(shí),q也變?yōu)闊o效了。在實(shí)際系統(tǒng)中,查找指向相同內(nèi)存的所有指針是異常困難的。(在這里,我們很難發(fā)現(xiàn)q已經(jīng)無效了)

    4.shared_ptr和new結(jié)合使用

    如果我們不初始化一個(gè)智能指針,它就會(huì)被初始化為一個(gè)空指針,如下面代碼所示,我們還可以用new返回的指針來初始化智能指針:

    shared_ptr<double>p1;//shared_ptr可以指向一個(gè)double shared_ptr<int>p2(new int(42));//p2指向一個(gè)值為42的int

    接受指針參數(shù)的智能指針構(gòu)造函數(shù)是explicit的,因此,我們不能將一個(gè)內(nèi)置指針隱式轉(zhuǎn)換為一個(gè)智能指針必須使用直接初始化形式來初始化一個(gè)智能指針:

    shared_ptr<int>p1 = new int(1024);//錯(cuò)誤,必須使用直接初始化 shared_ptr<int>p2(new int(1024));//正確,直接初始化 shared_ptr<int>clone(int p) {return new int(p);//錯(cuò)誤;隱式轉(zhuǎn)換為shared_ptr<int>; }shared_ptr<int>clone(int p) {return shared_ptr<int>(new int(p));//正確;顯式地用int*創(chuàng)建shared_ptr<int>; }

    默認(rèn)情況下,

  • 一個(gè)用來初始化智能指針的普通指針必須指向動(dòng)態(tài)內(nèi)存,因?yàn)橹悄苤羔樐J(rèn)使用delete釋放它所關(guān)聯(lián)的對象。
  • 我們可以將智能指針綁定到一個(gè)指向其他類型的資源的指針上,但是為了這樣做,必須提供自己的操作來替代delete。

  • 4.1.不要混合使用普通指針和智能指針

    shared_ptr可以協(xié)調(diào)對象的析構(gòu),但這僅限于其自身的拷貝(也是shared_ptr)之間。這也是為什么我們推薦使用make_shared而不是new的原因。這樣,我們就能在分配對象的同時(shí)就將shared_ptr與之綁定,從而避免了無意中將同一塊內(nèi)存綁定到多個(gè)獨(dú)立創(chuàng)建的shared_ptr上。

    現(xiàn)在讓我們看下面這段代碼:

    //在函數(shù)調(diào)用時(shí)ptr被創(chuàng)建并初始化 void process(shared_ptr<int>ptr) {//使用ptr };//ptr離開作用域,被銷毀

    process的參數(shù)是傳值方式傳遞,因此實(shí)參會(huì)被拷貝到ptr,創(chuàng)建一個(gè)shared_ptr會(huì)遞增其引用計(jì)數(shù),拷貝一個(gè)shared_ptr也會(huì)遞增其引用計(jì)數(shù),因此在process運(yùn)行過程中,其引用計(jì)數(shù)至少為2,等ptr離開作用域,ptr的引用計(jì)數(shù)會(huì)遞減,但不會(huì)變?yōu)?。因此,當(dāng)局部變量ptr被銷毀時(shí),ptr指向的內(nèi)存不會(huì)被釋放。

    使用process函數(shù)的正確方法是傳遞給它一個(gè)shared_ptr:

    shared_ptr<int>p(new int(42));//引用計(jì)數(shù)為1 process(p);//拷貝p會(huì)遞增引用計(jì)數(shù),引用計(jì)數(shù)為2 int i = *p;//正確,引用計(jì)數(shù)為1

    現(xiàn)在讓我們混合使用普通指針和智能指針,看會(huì)發(fā)生什么???

    雖然我們不能傳遞給process一個(gè)內(nèi)置指針,但可以傳遞給它一個(gè)(臨時(shí)的)shared_ptr,這個(gè)shared_ptr是用一個(gè)內(nèi)置指針顯式構(gòu)造的。但是,這樣做很可能會(huì)導(dǎo)致錯(cuò)誤:

    int *x(new int(1024));//危險(xiǎn),x是一個(gè)普通指針,不是一個(gè)智能指針 process(x);//錯(cuò)誤,不能將int*轉(zhuǎn)換為一個(gè)shared_ptr<int> process(shared_ptr<int>(x));//合法的,但內(nèi)存會(huì)被釋放 int j = *x;//未定義,x是一個(gè)空懸指針

    在上面的調(diào)用中,我們將一個(gè)臨時(shí)shared_ptr傳遞給process。當(dāng)這個(gè)調(diào)用所在的表達(dá)式結(jié)束時(shí),這個(gè)臨時(shí)對象就被銷毀了。銷毀這個(gè)臨時(shí)變量會(huì)遞減引用計(jì)數(shù),此時(shí)引用計(jì)數(shù)就變?yōu)?了。因此,當(dāng)臨時(shí)對象被銷毀時(shí),它所指向的內(nèi)存會(huì)被釋放。
    但x繼續(xù)指向(已經(jīng)釋放的)內(nèi)存,從而變成一個(gè)空懸指針。如果試圖使用x的值,其行為是未定義的。
    當(dāng)將一個(gè)shared_ptr綁定到一個(gè)普通指針時(shí),我們就將內(nèi)存的管理責(zé)任交給了這個(gè)shared_ptr。一旦這樣做了,我們就不應(yīng)該再使用內(nèi)置指針來訪問shared_ptr所指向的內(nèi)存了。

    注意:
    使用一個(gè)內(nèi)置指針來訪問一個(gè)智能指針?biāo)?fù)責(zé)的對象是很危險(xiǎn)的,因?yàn)槲覀儫o法知道對象何時(shí)會(huì)被銷毀。

    4.2.不要使用get初始化另一個(gè)智能指針或?yàn)橹悄苤羔樫x值

    智能指針類型定義了一個(gè)名為get的函數(shù)(參見表12.1),它返回一個(gè)內(nèi)置指針,指向智能指針管理的對象。

    當(dāng)我們需要向不能使用智能指針的代碼傳遞一個(gè)內(nèi)置指針時(shí),我們才會(huì)用get。

    使用get返回的指針的代碼不能delete此指針。

    雖然編譯器不會(huì)給出錯(cuò)誤信息,但將另一個(gè)智能指針也綁定到get返回的指針上是錯(cuò)誤的:

    shared_ptr<int>p(new int(42));//引用計(jì)數(shù)為1 int *q = p.get();//正確,但使用q時(shí)要注意,不要讓它管理的指針被釋放 {//新程序塊 //未定義:兩個(gè)獨(dú)立的shared_ptr指向相同的內(nèi)存shared_ptr<int>q; }//程序塊結(jié)束,q被銷毀,它指向的內(nèi)存被釋放 int foo = *p;//未定義:p指向的內(nèi)存已經(jīng)被釋放了

    在本例中,p和q指向相同的內(nèi)存。由于它們是相互獨(dú)立創(chuàng)建的,因此各自的引用計(jì)數(shù)都是1。當(dāng)q所在的程序塊結(jié)束時(shí),q被銷毀,這會(huì)導(dǎo)致q指向的內(nèi)存被釋放。從而p變成一個(gè)空懸指針,意味著當(dāng)我們試圖使用p時(shí),將發(fā)生未定義的行為。而且,當(dāng)p被銷毀時(shí),這塊內(nèi)存會(huì)被第二次delete。

    注意:
    get用來將指針的訪問權(quán)限傳遞給代碼,你只有在確定代碼不會(huì)delete指針的情況下,才能使用get。特別是,永遠(yuǎn)不要用get初始化另一個(gè)智能指針或者為另一個(gè)智能指針賦值。

    4.3.shared_ptr指針操作:reset

    上面的代碼,指針p指向的內(nèi)存已經(jīng)被釋放了,不過我們可以用reset來將一個(gè)新的指針賦予一個(gè)shared_ptr:

    p = new int(1024);//錯(cuò)誤,不能將一個(gè)指針賦予shared_ptr p.reset(new int(1024));//正確,p指向一個(gè)新對象

    與賦值類似,reset會(huì)更新引用計(jì)數(shù),如果需要的話,會(huì)釋放p指向的對象。**reset成員經(jīng)常與unique一起使用,來控制多個(gè)shared_ptr共享的對象。**在改變底層對象之前,我們檢查自己是否是當(dāng)前對象僅有的用戶。如果不是,在改變之前要制作一份新的拷貝:

    if (!p.unique())p.reset(new string(*p));//我們不是唯一用戶;分配新的拷貝 *p+=newVal;//現(xiàn)在我們知道自己是唯一的用戶,可以改變對象的值

    4.4.shared_ptr的刪除器

    本文暫時(shí)未介紹!!!

    5.智能指針和異常

    使用智能指針,即使程序塊過早結(jié)束或發(fā)生異常,智能指針類也能確保在內(nèi)存不再需要時(shí)將其釋放:

    void f() {shared_ptr<int>sp(new int(42));//分配一個(gè)對象//發(fā)生異常,且未被f捕獲 }//在函數(shù)結(jié)束時(shí)shared_ptr自動(dòng)釋放內(nèi)存

    函數(shù)的退出有兩種可能,正常處理結(jié)束或者發(fā)生了異常,無論哪種情況,局部對象都會(huì)被銷毀。在上面的程序中,sp是一個(gè)shared_ptr,因此sp銷毀時(shí)會(huì)檢查引用計(jì)數(shù)。在此例中,sp是指向這塊內(nèi)存的唯一指針,因此內(nèi)存會(huì)被釋放掉。
    與之相對的,當(dāng)發(fā)生異常時(shí),我們直接管理的內(nèi)存是不會(huì)自動(dòng)釋放的。如果使用內(nèi)置指針管理內(nèi)存,且在new之后在對應(yīng)的delete之前發(fā)生了異常,則內(nèi)存不會(huì)被釋放:

    void f {int *ip = new int(42);//動(dòng)態(tài)分配一個(gè)新對象//發(fā)生異常,且在f中未被捕獲delete ip//在退出之前手動(dòng)釋放內(nèi)存 }

    如果在new和delete之間發(fā)生異常,且異常未在f中被捕獲,則內(nèi)存就永遠(yuǎn)不會(huì)被釋放了。在函數(shù)f之外沒有指針指向這塊內(nèi)存,因此就無法釋放它了。

    5.1注意:智能指針陷阱

    智能指針可以提供對動(dòng)態(tài)分配的內(nèi)存安全而又方便的管理,但這建立在正確使用的前提下。為了正確使用智能指針,我們必須堅(jiān)持一些基本規(guī)范:

  • · 不使用相同的內(nèi)置指針值初始化(或reset)多個(gè)智能指針。
  • · 不delete get()返回的指針。
  • · 不使用get()初始化或reset另一個(gè)智能指針。
  • · 如果你使用get()返回的指針,記住當(dāng)最后一個(gè)對應(yīng)的智能指針銷毀后,你的指針就變?yōu)闊o效了。
  • ·如果你使用智能指針管理的資源不是new分配的內(nèi)存,記住傳遞給它一個(gè)刪除器(本文暫時(shí)沒有介紹該內(nèi)容)
  • 6.unique_ptr

    一個(gè)unique_ptr“擁有”它所指向的對象。與shared_ptr不同,某個(gè)時(shí)刻只能有一個(gè)unique_ptr指向一個(gè)給定對象。當(dāng)unique_ptr被銷毀時(shí),它所指向的對象也被銷毀。表12.4列出了unique_ptr特有的操作。與shared_ptr相同的操作列在表12.1)中。

    與shared_ptr不同,沒有類似make_shared的標(biāo)準(zhǔn)庫函數(shù)返回一個(gè)unique_ptr。當(dāng)我們定義一個(gè)unique_ptr時(shí),需要將其綁定到一個(gè)new返回的指針上。類似shared_ptr,初始化unique_ptr必須采用直接初始化形式:

    unique_ptr<double>p1;//可以指向一個(gè)double的unique_ptr unique_ptr<int>p2(new int(42));//p2指向一個(gè)值為42的int

    由于一個(gè)unique_ptr擁有它指向的對象,因此unique_ptr不支持普通的拷貝或賦值操作:

    unique_ptr<string>p1(new string("Stegosaurus")); unique_ptr<string>p2(p1);//錯(cuò)誤,unique_ptr不支持拷貝 unique_ptr<string>p3; p3 = p2;//錯(cuò)誤,unique不支持賦值

    雖然我們不能拷貝或賦值unique_ptr,但可以通過調(diào)用release或reset將指針的所有權(quán)從一個(gè)(非const)unique_ptr轉(zhuǎn)移給另一個(gè)unique:

    //將所有權(quán)從p1(指向string Stegosaurus)轉(zhuǎn)移給p2 unique_ptr<string>p2(p1.release());//release將p1置為空 unique_ptr<string>p3(new string("Trex")); //將所有權(quán)從p3轉(zhuǎn)移給p2 p2.reset(p3.release());//reset釋放了p2原來指向的內(nèi)存
  • release成員返回unique_ptr當(dāng)前保存的指針并將其置為空。
  • reset成員接受一個(gè)可選的指針參數(shù),令unique_ptr重新指向給定的指針。如果unique_ptr不為空,它原來指向的對象被釋放。
  • unique_ptr<string>p2(p1.release());

    因此,p2被初始化為p1原來保存的指針,而p1被置為空。

    p2.reset(p3.release());

    因此,對p2調(diào)用reset釋放了用"Stegosaurus"初始化的string所使用的內(nèi)存,將p3對指針的所有權(quán)轉(zhuǎn)移給p2,并將p3置為空。

    調(diào)用release會(huì)切斷unique_ptr和它原來管理的對象間的聯(lián)系。**release返回的指針通常被用來初始化另一個(gè)智能指針或給另一個(gè)智能指針賦值。**在本例中,管理內(nèi)存的責(zé)任簡單地從一個(gè)智能指針轉(zhuǎn)移給另一個(gè)。但是,如果我們不用另一個(gè)智能指針來保存release返回的指針,我們的程序就要負(fù)責(zé)資源的釋放:

    p2.release();//錯(cuò)誤,p2不會(huì)釋放內(nèi)存,而且我們丟失了指針 auto p = p2.release();//正確,但我們必須記得delete(p)

    6.1.傳遞unique_ptr參數(shù)和返回

    unique_ptr不能拷貝unique_ptr的規(guī)則有一個(gè)例外:我們可以拷貝或賦值一個(gè)將要被銷毀的unique_ptr。最常見的例子是從函數(shù)返回一個(gè)unique_ptr:

    unique_ptr<int>clone(int p) {//正確,從int*創(chuàng)建一個(gè)unique_ptr<int>return unique_ptr<int>(new int(p)); }

    還可以返回一個(gè)局部對象的拷貝:

    unique_ptr<int>clone(int p) {unique_ptr<int>ret(new int (p));//...return ret; }

    對于兩段代碼,編譯器都知道要返回的對象將要被銷毀。在此情況下,編譯器執(zhí)行一種特殊的“拷貝”(本文無介紹)

    6.2.unique_ptr的刪除器

    本文暫時(shí)不介紹!!!

    7.weak_ptr

    weak_ptr(見表12.5)是一種不控制所指向?qū)ο笊嫫诘闹悄苤羔?#xff0c;它指向由一個(gè)shared_ptr管理的對象。將一個(gè)weak_ptr綁定到一個(gè)shared_ptr不會(huì)改變shared_ptr的引用計(jì)數(shù)。一旦最后一個(gè)指向?qū)ο蟮膕hared_ptr被銷毀,對象就會(huì)被釋放。即使有weak_ptr指向?qū)ο?#xff0c;對象也還是會(huì)被釋放,因此,weak_ptr的名字抓住了這種智能指針“弱”共享對象的特點(diǎn)。
    當(dāng)我們創(chuàng)建一個(gè)weak_ptr時(shí),要用一個(gè)shared_ptr來初始化它:

    auto p = make_shared<int>(42); weak_ptr<int>wp(p);//wp弱共享p;p的引用計(jì)數(shù)未改變

    本例中wp和p指向相同的對象。由于是弱共享,創(chuàng)建wp不會(huì)改變p的引用計(jì)數(shù);wp指向的對象可能被釋放掉。
    由于對象可能不存在,我們不能使用weak_ptr直接訪問對象,而必須調(diào)用lock此函數(shù)檢查weak_ptr指向的對象是否仍存在。如果存在,lock返回一個(gè)指向共享對象的shared_ptr。與任何其他shared_ptr類似,只要此shared_ptr存在,它所指向的底層對象也就會(huì)一直存在。例如:

    auto p = make_shared<int>(42); weak_ptr<int>wp(p);//wp弱共享p;p的引用計(jì)數(shù)未改變 if(shared_ptr<int>np = wp.lock())//如果np不為空則條件成立 {//在if中,np與p共享對象 }

    在這段代碼中,只有當(dāng)lock調(diào)用返回true時(shí)我們才會(huì)進(jìn)入if語句體。在if中,使用np訪問共享對象是安全的。

    7.1.核查指針類

    作為weak_ptr用途的一個(gè)展示,我們將為StrBlob類定義一個(gè)伴隨指針類。我們的指針類將命名為StrBlobPtr,會(huì)保存一個(gè)weak_ptr,指向StrBlob的data成員,這是初始化時(shí)提供給它的。通過使用weak_ptr,不會(huì)影響一個(gè)給定的StrBlob所指向的vector的生存期。但是,可以阻止用戶訪問一個(gè)不再存在的vector的企圖。
    StrBlobPtr會(huì)有兩個(gè)數(shù)據(jù)成員:wptr,或者為空,或者指向一個(gè)StrBlob中的vector;curr,保存當(dāng)前對象所表示的元素的下標(biāo)。類似它的伴隨類StrBlob,我們的指針類也有一個(gè)check成員來檢查解引用StrBlobPtr是否安全:

    //對于訪問一個(gè)不存在元素的嘗試,StrBlobPtr拋出一個(gè)異常 class StrBlobPtr {public:StrBlobPtr():curr(0){}StrBlobPtr(StrBlob &a,size_t sz = 0):wptr(a.data),curr(sz){}std::string &deref()const;StrBlobPtr&incr();//前綴遞增private://若檢查成功,check返回一個(gè)指向vector的shared_ptrstd::shared_ptr<std::vector<std::string>>check(std::size_t,const std::string&)const;//保存一個(gè)weak_ptr,意味著底層vector可能會(huì)被銷毀std::weak_ptr<std::vector<std::string>>wptr;std::size_t curr;//在數(shù)組中的當(dāng)前位置 };

    默認(rèn)構(gòu)造函數(shù)生成一個(gè)空的StrBlobPtr。其構(gòu)造函數(shù)初始化列表將curr顯式初始化為0,并將wptr隱式初始化為一個(gè)空weak_ptr。第二個(gè)構(gòu)造函數(shù)接受一個(gè)StrBlob引用和一個(gè)可選的索引值。此構(gòu)造函數(shù)初始化wptr,令其指向給定StrBlob對象的shared_ptr中的vector,并將curr初始化為sz的值。我們使用了默認(rèn)參數(shù),表示默認(rèn)情況下將curr初始化為第一個(gè)元素的下標(biāo)。我們將會(huì)看到,StrBlob的end成員將會(huì)用到參數(shù)sz。

    值得注意的是,我們不能將StrBlobPtr綁定到一個(gè)const StrBlob對象。這個(gè)限制是由于構(gòu)造函數(shù)接受一個(gè)非const StrBlob對象的引用而導(dǎo)致的。
    StrBlobPtr的check成員與StrBlob中的同名成員不同,它還要檢查指針指向的vector是否還存在:

    std::shared_ptr<std::vector<std::string>> StrBlobPtr::check(std::size_t i,const std::string &msg) const {auto ret = wptr.lock();//vector還存在嗎?if (!ret)throw std::runtime_error("unbound StrBlobPtr");if (i >= ret->size())throw std::out_of_range(msg);return ret; //否則,返回指向vector的shared_ptr }

    由于一個(gè)weak_ptr不參與其對應(yīng)的shared_ptr的引用計(jì)數(shù),StrBlobPtr指向的vector可能已經(jīng)被釋放了。如果vector已銷毀,lock將返回一個(gè)空指針。在本例中,任何vector的引用都會(huì)失敗,于是拋出一個(gè)異常。否則,check會(huì)檢查給定索引,如果索引值合法,check返回從lock獲得的shared_ptr。

    7.2.指針操作

    在這里我們不用重載運(yùn)算符的方法,我們定義名為deref和incr的函數(shù),分別用來解引用和遞增StrBlobPtr。
    deref成員調(diào)用check,檢查使用vector是否安全以及curr是否在合法范圍內(nèi):

    std::string&StrBlobPtr::deref()const {auto p = check(curr,"dereference past end");return (*p)[curr];//(*p)是對象所指向的vector }

    如果check成功,p就是一個(gè)shared_ptr,指向StrBlobPtr所指向的vector。表達(dá)式(*p)[curr]解引用shared_ptr來獲得vector,然后使用下標(biāo)運(yùn)算符提取并返回curr位置上的元素。

    //incr成員也調(diào)用check //前綴遞增;返回遞增后的對象的引用 StrBlobPtr&StrBlobPtr::incr() {//如果curr已經(jīng)指向容器的尾后位置,就不能遞增它check(curr,"increment past end of StrBlobPtr");++curr;//推進(jìn)當(dāng)前位置return *this; }

    當(dāng)然,為了訪問data成員,我們的指針類必須聲明為StrBlob的friend(參見7.3.4節(jié),第250頁)。我們還要為StrBlob類定義begin和end操作,返回一個(gè)指向它自身的StrBlobPtr:

    //對于StrBlob中的友元聲明來說,此前置聲明是必要的 class StrBlobPtr; class StrBlob {friend class StrBlobPtr;//返回指向首元素和后元素的StrBlobPtrStrBlobPtr begin(){return StrBlobPtr (*this);}StrBlobPtr end(){auto ret = StrBlobPtr(*this,data->size());return ret;} };

    7.3.StrBlobPtr類的測試

    完整代碼如下:

    #ifndef MY_STRBLOB_H #define MY_STRBLOB_H #include <vector> #include <string> #include <initializer_list> #include <memory> #include <stdexcept>using namespace std;//提前聲明,StrBlob中的友類聲明所需 class StrBlobPtr;class StrBlob {friend class StrBlobPtr;public:typedef vector<string>::size_type size_type;StrBlob();StrBlob(initializer_list<string>il);size_type size()const {return data->size();}bool empty()const {return data->empty();}//添加和刪除元素void push_back(const string &t) {data->push_back(t);}void pop_back();//元素訪問string &front();const string &front()const;string &back();const string &back()const;//提供給StrBlobPtr的接口StrBlobPtr begin();//這是聲明,定義StrBlobPtr后才能定義這兩個(gè)函數(shù)StrBlobPtr end();private:shared_ptr<std::vector<std::string>>data;//如果data[i]不合法,拋出一個(gè)異常void check(size_type i, const std::string &msg)const; };inline StrBlob::StrBlob(): data(make_shared<vector<string>>()) {}StrBlob::StrBlob(initializer_list<string>il): data(make_shared<vector<string>>(il)) {}inline void StrBlob::check(size_type i, const string &msg) const {if (i >= data->size())throw out_of_range(msg); }inline string&StrBlob::front() {//如果vector為空,check會(huì)拋出一個(gè)異常check(0,"front on empty StrBlob");return data->front(); }//const版本front inline const string &StrBlob::front()const {check(0, "front on empty StrBlob");return data->front(); }inline string &StrBlob::back() {check(0, "back on empty StrBlob");return data->back(); }//const 版本 back inline const string &StrBlob::back()const {check(0, "back on empty StrBlob");return data->back(); }inline void StrBlob::pop_back() {check(0, "pop_back on empty StrBlob");data->pop_back(); }//當(dāng)試圖訪問一個(gè)不存在的元素時(shí),StrBlobPtr拋出一個(gè)異常 class StrBlobPtr {friend bool eq(const StrBlobPtr &, const StrBlobPtr &);public:StrBlobPtr(): curr(0) {}StrBlobPtr(StrBlob &a, size_t sz = 0): wptr(a.data), curr(sz) {}string &deref()const;StrBlobPtr &incr();//前綴遞增StrBlobPtr &decr();//前綴遞減private://若檢查成功,check返回一個(gè)指向vector的shared_ptrshared_ptr<vector<string>>check(size_t, const string &)const;//保存一個(gè)weak_ptr,意味著底層vector可能會(huì)被銷毀weak_ptr<vector<string>>wptr;size_t curr;//在數(shù)組中的當(dāng)前位置 };inline shared_ptr<vector<string>> StrBlobPtr::check(size_t i, const string &msg)const {auto ret = wptr.lock();//vector還存在嗎?if (!ret)throw runtime_error("unbound StrBlobPtr");if (i >= ret->size())throw out_of_range(msg);return ret; }inline string &StrBlobPtr::deref()const {auto p = check(curr, "dereference past end");return (*p)[curr]; }//前綴遞增:返回遞增后的對象的引用 inline StrBlobPtr &StrBlobPtr::incr() {//如果curr已經(jīng)指向容器的尾后位置,就不能遞增它check(curr, "increment past end of StrBlobPtr");++curr;//推進(jìn)當(dāng)前位置return *this; }//前綴遞減:返回遞減后的對象的引用 inline StrBlobPtr &StrBlobPtr::decr() {//如果curr已經(jīng)為0,遞減它就會(huì)產(chǎn)生一個(gè)非法下標(biāo)--curr; //遞減當(dāng)前位置check(-1, "decrement past begin of StrBlobPtr");return *this; }//StrBlob的begin和end成員的定義 inline StrBlobPtr StrBlob::begin() {return StrBlobPtr(*this); }inline StrBlobPtr StrBlob::end() {auto ret = StrBlobPtr(*this, data->size());return ret; }//StrBlobPtr的比較操作 inline bool eq(const StrBlobPtr &lhs, const StrBlobPtr &rhs) {auto l = lhs.wptr.lock(), r = rhs.wptr.lock();//若底層的vector是同一個(gè)if (l == r)//則兩個(gè)指針都是空,或者指向相同的元素時(shí),它們相等return (!r || lhs.curr == rhs.curr);elsereturn false;//若指向不同vector,則不可能相等 }inline bool neq(const StrBlobPtr &lhs, const StrBlobPtr &rhs) {return !eq(lhs, rhs); } #endif

    主程序代碼:

    #include <iostream> using namespace std; #include "my_StrBlob.h"int main(int argc, char **argv) {StrBlob b1;{StrBlob b2 = {"a", "an", "the"};b1 = b2;b2.push_back("about");cout << b2.size() << endl;}cout << b1.size() << endl;cout << b1.front() << " " << b1.back() << endl;const StrBlob b3 = b1;cout << b3.front() << " "<<b3.back() << endl;for (auto it = b1.begin(); neq(it, b1.end()); it.incr())cout << it.deref() << endl;return 0; }

    測試結(jié)果:

    這次我們用文件讀入的方式測試:

    代碼如下:

    #include <iostream> #include <fstream> using namespace std;#include "my_StrBlob.h"int main(int argc, char **argv) {ifstream in(argv[1]);if (!in) {cout << "無法打開輸入文件" << endl;return -1;}StrBlob b;string s;while (getline(in, s))b.push_back(s);for (auto it = b.begin(); neq(it, b.end()); it.incr())cout << it.deref() << endl;system("pause");return 0; }

    7.4.小插曲:改進(jìn)StrBlobPtr的構(gòu)造函數(shù)

    我們不能將StrBlobPtr綁定到一個(gè)const StrBlob對象。這個(gè)限制是由于構(gòu)造函數(shù)接受一個(gè)非const StrBlob對象的引用而導(dǎo)致的

    現(xiàn)在我們來設(shè)計(jì)一個(gè)可以接受const StrBlob對象的引用的構(gòu)造函數(shù)。

    首先,為StrBlobPtr定義能接受const StrBlob &參數(shù)的構(gòu)造函數(shù):

    StrBlob(const StrBlob &a,size_t sz = 0):wptr(a.data),curr(sz){}

    其次,為StrBlob定義能操作const對象的begin和end。

    聲明:

    StrBlobPtr begin() const; StrBlobPtr end() const;

    定義:

    inline StrBlobPtr StrBlob::begin()const {return StrBlobPtr(*this); }inline StrBlobPtr StrBlob::end()const {auto ret = StrBlobPtr(*this,data->size());return ret; }
  • 本人水平有限,如有錯(cuò)誤,還請?jiān)谠u(píng)論區(qū)留言指出,謝謝!!!
  • 創(chuàng)作不易,留個(gè)贊再走唄!!!
  • 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

    總結(jié)

    以上是生活随笔為你收集整理的2.5w字长文爆肝 C++动态内存与智能指针一篇搞懂!太顶了!!!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

    九九热免费在线视频 | 一区二区视频电影在线观看 | 国产精品不卡在线播放 | 国产综合精品一区二区三区 | www最近高清中文国语在线观看 | 国产一区成人在线 | 国产日产高清dvd碟片 | 亚洲精品久久久久中文字幕二区 | 天无日天天操天天干 | 欧美精品乱码久久久久久 | 正在播放一区 | 日韩高清无线码2023 | 黄色网址在线播放 | 中文字幕在线播出 | 97视频人人澡人人爽 | 天堂网在线视频 | 午夜精品视频一区二区三区在线看 | 日韩av黄 | 香蕉久久国产 | 91av免费在线观看 | 国产资源免费在线观看 | 久久精品亚洲综合专区 | 日韩精品中文字幕在线不卡尤物 | 91免费观看国产 | 国产xxxx做受性欧美88 | 五月婷婷一区二区三区 | 国产亚洲精品久久 | 五月婷婷影院 | 国产乱对白刺激视频在线观看女王 | 久久97久久97精品免视看 | 中文字幕日韩免费视频 | 夜色.com| 欧美射射射 | 日韩欧美在线视频一区二区三区 | 久久久久久久久久福利 | 日本黄色免费在线观看 | 日韩免费三级 | 男女精品久久 | 亚洲一区二区三区四区精品 | 色婷婷免费视频 | 日韩一区二区三区高清免费看看 | 国产精品91一区 | 国产美女黄网站免费 | 西西4444www大胆无视频 | 高清在线一区二区 | 狠狠干激情| 免费福利在线播放 | 在线成人小视频 | 高清中文字幕 | 91成人区 | 亚洲人久久| 精品国产乱码久久久久久久 | 国产免费二区 | 国产最新视频在线观看 | 国产精品黄色影片导航在线观看 | 天天操天天爱天天干 | 五月婷在线视频 | 五月综合激情婷婷 | 国产精久久 | 午夜精品久久久久久久爽 | 激情欧美一区二区三区免费看 | 色之综合网 | 国产精品视频免费在线观看 | 操少妇视频 | 99久久夜色精品国产亚洲 | 四虎在线视频 | 成人毛片在线观看 | 四虎免费av| 天天干天天草天天爽 | 中文字幕第一页在线视频 | av解说在线 | 亚洲二区精品 | 99精品在线视频观看 | 中文字幕亚洲欧美 | 国产精品婷婷午夜在线观看 | 欧美最猛性xxxxx免费 | 最近高清中文字幕在线国语5 | 亚洲精品在线观看免费 | 成人丝袜 | 久久久久 免费视频 | 久久私人影院 | 亚洲视频456 | 六月婷操 | 中文字幕在线资源 | 97超碰人人网 | 99视频在线观看一区三区 | 久久久久免费精品国产 | 成人黄色免费在线观看 | 国内精品视频一区二区三区八戒 | 在线免费黄色片 | ww亚洲ww亚在线观看 | 日韩精品免费一区二区在线观看 | 不卡视频在线看 | 天天干天天干天天射 | 男女激情麻豆 | 国产一卡二卡在线 | 欧美视频99 | 日韩欧美视频免费在线观看 | 最近最新mv字幕免费观看 | 69国产在线观看 | 亚洲国产网址 | 99久久精品免费看国产四区 | 中文字幕精品一区二区三区电影 | 五月天激情电影 | 丝袜美腿一区 | 久久婷婷一区二区三区 | www九九热 | 国产在线观看污片 | 在线免费看黄网站 | 日本久久久精品视频 | 国产在线观| 亚洲一区免费在线 | 中文字幕第一页在线播放 | 在线观看中文av | 亚洲精品黄色片 | 日韩在线一二三区 | 成人午夜电影在线播放 | 国产一区二区三精品久久久无广告 | 婷婷丁香激情五月 | 色瓜| 欧美在线视频一区二区三区 | 欧美天天干 | 国产成人三级在线 | 国产黄色一级大片 | 成人久久国产 | 黄色成人91 | 成人午夜网 | 国产成人精品一区二区三区在线 | 欧美激情视频一区 | 97夜夜澡人人爽人人免费 | 久久久久久国产一区二区三区 | 欧美日韩国产色综合一二三四 | 成人午夜在线电影 | 久久免费一级片 | 中文字幕 国产专区 | 国产一区二区在线观看视频 | 六月丁香激情综合色啪小说 | 亚洲一区二区三区miaa149 | 久久天堂精品视频 | 国产精品美女免费看 | 91成人看片 | 久久中文精品视频 | 国产精品久久99综合免费观看尤物 | 欧美日韩视频观看 | 久久久久久欧美二区电影网 | 五月天婷婷丁香花 | 不卡av在线免费观看 | 黄色大全免费观看 | 在线免费中文字幕 | 天天爱天天操天天射 | 久久精品国产一区二区 | 超碰97在线人人 | 欧美性一级观看 | 97在线视频观看 | 免费97视频 | 96视频在线 | 日日躁夜夜躁xxxxaaaa | 久久久久国产精品午夜一区 | 色偷偷88888欧美精品久久 | 国产免费叼嘿网站免费 | 久久艹在线 | 狠狠操综合网 | 天天操天天射天天爽 | 亚洲精品ww | 精品国产视频在线 | 欧美极品少妇xbxb性爽爽视频 | 青青河边草观看完整版高清 | 国产免费中文字幕 | 国产色在线观看 | 国产亚洲成人网 | 日韩专区av | 91九色国产蝌蚪 | 色欧美综合| 激情在线免费视频 | 伊人精品在线 | 在线日韩中文 | av在线一二三区 | 亚洲黄色一级大片 | 操操碰 | 超碰在线1 | 亚洲综合视频在线观看 | 国产 日韩 欧美 在线 | 国产丝袜一区二区三区 | 国产色综合天天综合网 | 午夜手机看片 | 黄色av免费看 | 中文区中文字幕免费看 | 97免费公开视频 | 狠狠躁夜夜躁人人爽超碰91 | 久久99热这里只有精品 | av三级av| 顶级欧美色妇4khd | 色欲综合视频天天天 | 久久精品牌麻豆国产大山 | 亚洲激情婷婷 | 久精品视频在线观看 | 日韩最新在线 | www.神马久久 | 久久综合免费视频影院 | 麻豆综合网 | 久久综合九色综合网站 | 五月天综合网站 | 一区二区三区四区精品 | 亚洲视频 视频在线 | 8x8x在线观看视频 | 中国一级片在线观看 | 99精品成人 | 国产精品手机视频 | 免费看的黄网站 | 国产精品久久片 | 国产999久久久 | 国产精品久久久av | 9797在线看片亚洲精品 | 国产成人精品电影久久久 | 久国产在线播放 | 久久综合中文字幕 | 97超碰资源 | 国产看片免费 | 欧美91av | 亚洲精品中文在线资源 | 久久激情五月丁香伊人 | 欧美一级片在线观看视频 | 国产视频18 | 国产91成人 | wwwwwww色| 国产午夜在线 | 欧美在线观看视频一区二区三区 | 91免费国产在线观看 | 三级在线视频观看 | 久久av不卡 | adc在线观看 | 99超碰在线观看 | 亚洲日本中文字幕在线观看 | 免费黄色在线播放 | 免费观看成年人视频 | 久久久久久久久久久久av | 国产黄色精品网站 | 国产精品毛片一区视频播不卡 | 国产99久久久国产精品成人免费 | 黄色网址av | 天天做天天爱夜夜爽 | 国产精品99久久久久久久久久久久 | 国产免费xvideos视频入口 | 国产精品久久99综合免费观看尤物 | 精品久久久久久久久久久院品网 | 久草视频在线资源站 | 天天躁天天操 | 亚洲一级免费观看 | 玖玖精品在线 | 国产在线视频不卡 | 91久久精品一区 | 精品久久国产 | 中文字幕婷婷 | 久久国产露脸精品国产 | 久久精品电影院 | 亚洲精品久久久久中文字幕二区 | 手机在线小视频 | 黄色国产高清 | 日日弄天天弄美女bbbb | 久久99精品热在线观看 | 国产精品免费人成网站 | 欧美 另类 交 | 国产精品一二 | 亚洲欧美日韩一二三区 | 亚洲精品99久久久久中文字幕 | 久久免费久久 | 日韩三级在线观看 | 综合网婷婷 | 久久久久国产视频 | 久久久亚洲网站 | 国内小视频 | 欧美国产日韩在线视频 | 欧美精品乱码久久久久久 | 99视频在线观看免费 | 国产精品麻豆一区二区三区 | 伊人伊成久久人综合网小说 | 国产精品色在线 | 性色av一区二区三区在线观看 | 中文字幕乱在线伦视频中文字幕乱码在线 | 国产亚洲成av人片在线观看桃 | 成年在线观看 | 色综合久久久久综合体 | 懂色av懂色av粉嫩av分享吧 | 最近免费观看的电影完整版 | 国产精品永久 | 亚洲成熟女人毛片在线 | 久久成年人 | 国产免费一区二区三区最新 | 国产一区二区久久精品 | 国产在线观看国语版免费 | 亚洲一区精品二人人爽久久 | 看片黄网站 | 青草视频在线看 | 欧美日韩精品影院 | 日韩av高清 | 欧美日韩aa| 亚洲激情 | 亚洲精品在线视频播放 | av电影不卡在线 | 91精品国产三级a在线观看 | 婷婷在线看 | av在线电影播放 | 欧美日韩免费在线视频 | av中文字幕在线看 | 麻豆视频免费播放 | 鲁一鲁影院 | 久久精品视频在线看 | 极品嫩模被强到高潮呻吟91 | 天天干天天操天天拍 | www.天天色.com | 日本精品一区二区三区在线观看 | 亚洲精品在线免费观看视频 | 婷婷色综合 | www.夜夜骑.com| 国精产品永久999 | 最新99热 | 美女av在线免费 | 欧美日韩国产精品一区 | 精品国产亚洲一区二区麻豆 | 在线国产精品视频 | 色99之美女主播在线视频 | 在线日韩一区 | 国产精品福利久久久 | 中文字幕av专区 | 日韩理论在线视频 | 91视频在线观看免费 | 丁香花在线观看视频在线 | 99r在线精品 | 97视频在线免费观看 | 天天天天色综合 | 五月婷婷六月丁香在线观看 | 日韩一区二区三区不卡 | 人人添人人澡人人澡人人人爽 | 国产成人一区二区在线观看 | av网站免费看 | 天堂在线一区二区三区 | 欧美成人一二区 | av综合 日韩 | 日韩剧 | 国产精品久久久一区二区三区网站 | 色五月色开心色婷婷色丁香 | 99这里只有 | 在线观看亚洲国产精品 | 欧美精品久久久久久久久久丰满 | 欧美地下肉体性派对 | 亚洲国产日韩av | 亚洲视频网站在线观看 | 欧美精品二 | 欧美日韩观看 | 国产精品高潮久久av | 夜色成人av| 免费看成人 | 香蕉网站在线观看 | 日韩精品影视 | 日一日干一干 | av3级在线 | 日韩精品一区二区三区在线视频 | 欧美一级片在线 | 一区二区三区视频在线 | 国产精品国内免费一区二区三区 | 国产免费又爽又刺激在线观看 | 999国产| 欧美成人高清 | 精品福利av | 亚洲综合色丁香婷婷六月图片 | 麻豆精品视频在线观看免费 | 九九免费精品 | 亚洲免费精品视频 | 五月婷婷丁香色 | 精品国产成人在线影院 | 精品久久99| 久久国产精品免费观看 | 色国产视频 | 麻豆视频免费观看 | 婷婷五天天在线视频 | 久久久久国产视频 | 精品在线观 | 精品爱爱 | 在线国产视频观看 | 国产精品毛片久久 | .国产精品成人自产拍在线观看6 | 日韩免费播放 | 日韩在线一二三区 | 插婷婷 | 9在线观看免费高清完整版 玖玖爱免费视频 | 日韩精品一区二区电影 | 成人午夜久久 | 久久不色 | 黄色成人影视 | 久久精品99国产 | 成人av在线看 | 成人动漫视频在线 | 一区二区三区精品在线 | 国产亚洲综合性久久久影院 | 不卡的一区二区三区 | 国内精品视频在线播放 | 综合激情伊人 | 亚洲黄色av一区 | 99精品视频在线播放观看 | 国产精品毛片一区二区在线 | 久久久久亚洲国产 | 在线久久 | 一本到视频在线观看 | 97久久精品午夜一区二区 | 欧美精品久久久久久久 | 日韩经典一区二区三区 | 999久久国精品免费观看网站 | 手机在线看a | 中文日韩在线 | 人人爱人人爽 | 99在线高清视频在线播放 | 午夜久久福利影院 | 国产精品丝袜在线 | 国产精品入口a级 | 中国黄色一级大片 | 午夜美女影院 | 亚洲第一区精品 | 精品久久久久久久久久岛国gif | 91成熟丰满女人少妇 | 国产剧情久久 | 欧美日韩在线观看视频 | 国产精品乱码久久久久久1区2区 | 久久久久久久久久电影 | 在线看小早川怜子av | 精品久久1 | 免费在线看v | 精品国产免费久久 | 91精品亚洲影视在线观看 | 四虎国产精品成人免费影视 | 中文字幕视频观看 | 麻豆播放 | 国产精品久久久久久久久大全 | 青春草免费视频 | 国产精品一区二区美女视频免费看 | 国产一区精品在线 | 高潮久久久 | 日韩高清国产精品 | 久久激情精品 | 午夜色性片 | 日韩精品一区二区免费视频 | 久久av中文字幕片 | 久久久美女 | 91av官网 | 久久久久亚洲精品成人网小说 | www日日| 亚洲一区二区黄色 | 97精品国产一二三产区 | av不卡免费在线观看 | 久久精品96 | 欧美日韩久久不卡 | 成人免费在线看片 | 天天碰天天操 | 狠狠操操操 | 在线观看国产日韩欧美 | 五月婷婷中文网 | 成人午夜剧场在线观看 | 成人午夜电影在线 | 天天综合操 | 久久久免费观看视频 | 欧美日韩xxxxx | 成人黄色在线视频 | 欧美一级高清片 | 美女视频黄在线观看 | 亚洲高清国产视频 | 999精品在线| 亚洲天堂网视频在线观看 | 成人91在线 | 伊人干综合 | 懂色av一区二区三区蜜臀 | 超碰九九 | 久久夜色精品国产欧美一区麻豆 | av电影免费看 | 亚洲国产黄色片 | 国产成人久久av免费高清密臂 | 欧美国产高清 | 成人a级黄色片 | 五月婷婷爱 | 这里只有精品视频在线 | 色噜噜日韩精品一区二区三区视频 | 精品视频久久 | 激情欧美一区二区三区 | 国产一线在线 | 色婷婷国产 | 狠狠干干| 91精品国自产在线 | 99久久婷婷国产一区二区三区 | 91最新在线 | 中文字幕av全部资源www中文字幕在线观看 | 国产精品美女免费 | 国内少妇自拍视频一区 | 国产精品美女久久久久aⅴ 干干夜夜 | 久久国语露脸国产精品电影 | 日本在线观看一区二区 | 国产精品一区免费看8c0m | 欧美午夜性生活 | 国产破处精品 | 中文字幕久久精品 | 久操操| 日韩免费看| 久久久久久久久久网 | 婷婷午夜激情 | 99成人免费视频 | 国产免费一区二区三区最新 | 天天操天天干天天操天天干 | 手机看片1042 | 国产精品自产拍在线观看蜜 | 中文字幕一区二区三 | 国产在线a免费观看 | 中文字幕免费在线看 | 色综合中文字幕 | 97在线影院| 一区二区三区精品在线 | 久久艹影院 | 精品国产一二三 | 91探花在线| 亚洲精选在线观看 | 西西人体www444 | 西西4444www大胆视频 | 国产特级毛片aaaaaaa高清 | 成人午夜精品福利免费 | 91看片淫黄大片一级在线观看 | 最近中文字幕久久 | 日韩精品在线看 | 精品福利网 | 国产免费又黄又爽 | av在线电影播放 | 成年人app网址 | 精品国产一区二区三区久久久 | 久久精品中文字幕 | 国产第一页精品 | 久久精品视频网 | 美女黄网久久 | 日本三级不卡视频 | 婷婷六月久久 | 久久久久久久久久久国产精品 | 在线之家免费在线观看电影 | 六月激情丁香 | 久久99精品国产麻豆宅宅 | 亚洲视频 视频在线 | 精品久久99 | 亚洲 欧美变态 另类 综合 | 97小视频 | 在线视频日韩欧美 | 天天干天天搞天天射 | 国产成人精品日本亚洲999 | 久久综合免费视频影院 | 免费一级片观看 | 激情 婷婷 | 中文字幕日韩国产 | 视频在线观看亚洲 | 国产精品9区 | 午夜在线免费观看 | 欧美另类网站 | 午夜久久影视 | 黄影院| 韩国av电影在线观看 | 五月天激情综合 | 久久99影院 | 在线日韩精品视频 | 亚洲国产精彩中文乱码av | 色狠狠狠| 亚洲伊人成综合网 | 欧美片一区二区三区 | 国内一区二区视频 | 色综合小说 | 亚洲一二三久久 | 欧美色精品天天在线观看视频 | 成人h电影 | 久久久av电影 | 日韩精品久久久久久久电影99爱 | 成人综合婷婷国产精品久久免费 | 国产成人高清 | 中文字幕在线日 | 国产福利a | 91色吧 | 丁香综合激情 | 日韩视频免费在线观看 | 欧美不卡视频在线 | 欧美a级成人淫片免费看 | 色5月婷婷| 四虎视频| 日韩免费电影网 | 夜夜躁日日躁 | 精品视频免费播放 | 91亚洲精品久久久蜜桃借种 | 91中文在线 | 日韩中文字幕在线不卡 | 国产午夜精品福利视频 | 久久亚洲二区 | 毛片1000部免费看 | 九九在线国产视频 | 日韩免费在线视频观看 | 黄色资源网站 | 五月婷网| 久久观看最新视频 | 久久婷婷国产色一区二区三区 | 日本精品视频一区二区 | 国产成人777777 | 黄色www免费 | 国产精品不卡av | www.色午夜.com | 六月丁香激情综合色啪小说 | 99九九视频 | 国产精品乱码久久久久 | 国产高清在线永久 | 中文国产在线观看 | 国产精品毛片久久久久久久 | 日韩av影视在线观看 | 欧美特一级| 免费人成在线观看网站 | 欧美午夜寂寞影院 | 日韩av片免费在线观看 | 久久久久国产精品免费网站 | 欧美激情va永久在线播放 | 国产精久久久久久妇女av | 国产免费激情久久 | 在线视频精品播放 | 亚洲一区二区三区在线看 | 精品国产一区二区三区久久久蜜月 | 一区二区在线影院 | 国产精品一二三 | 美女视频黄网站 | 99久久精品无免国产免费 | 色综合久久久久久中文网 | 在线观看黄色免费视频 | 亚洲精品视频在线观看网站 | 亚洲综合视频网 | 免费网站在线观看人 | 激情视频免费观看 | 亚洲婷婷综合色高清在线 | 一区二区三区在线影院 | 日韩特级毛片 | 久草线| 五月天高清欧美mv | 九九导航 | 97超级碰碰| 日日夜夜av | av片子在线观看 | 国产精品久久久av | 五月天综合激情 | 日韩精品免费在线观看视频 | 91麻豆网站 | 欧美在线一二 | 男女啪啪免费网站 | 国产五月色婷婷六月丁香视频 | 99在线精品视频 | 最新日本中文字幕 | av一区二区三区在线观看 | 久久人人爽视频 | 亚洲电影在线看 | 中文字幕资源网 国产 | 欧美日韩国产在线 | 亚洲一区视频在线播放 | 日韩www在线 | 国模吧一区 | 国产小视频你懂的 | 国产又粗又猛又黄又爽 | 国产又粗又长的视频 | 免费观看av网站 | 最新国产视频 | 福利视频第一页 | 亚洲欧美视频在线播放 | 国产精品一区二区三区免费看 | 在线免费观看视频你懂的 | 日本精品小视频 | 狠狠干电影 | 精品视频免费 | 国产精品麻豆三级一区视频 | 五月天天av | www国产亚洲精品 | 日韩欧美区 | 九九影视理伦片 | 国产成人久久精品77777综合 | 麻豆国产在线播放 | 在线观看免费日韩 | 天天搞天天干 | 91麻豆.com| 久久久www成人免费精品张筱雨 | 在线看片日韩 | 久久国产精品影视 | 黄色福利网 | 麻豆视频一区二区 | 91九色视频在线播放 | 国产精品美女视频 | 夜夜高潮夜夜爽国产伦精品 | 国产成人精品在线 | 日韩精品视频免费专区在线播放 | 国产高清在线免费视频 | 免费看污污视频的网站 | 国产美女精品久久久 | 国产97免费| 91精品一区二区三区蜜臀 | 国产精品video爽爽爽爽 | 欧美伦理电影一区二区 | 久久伦理电影网 | 久草免费在线视频 | 九色琪琪久久综合网天天 | 天天做综合网 | 国产精品久久久av久久久 | 五月天丁香视频 | 亚洲狠狠 | 中文字幕第一页在线视频 | 亚洲日本三级 | 欧美精品久久人人躁人人爽 | 日韩美女免费线视频 | 高清免费av在线 | 日韩欧美一区二区三区在线观看 | 婷婷色资源 | 黄色免费av | 国产精品18毛片一区二区 | 成人影音av| 激情久久一区二区三区 | 亚洲午夜电影网 | 久久任你操 | 日韩视频1 | 中文字幕精 | 最新真实国产在线视频 | 日韩在线观看a | 久久国语露脸国产精品电影 | 久久99精品国产麻豆宅宅 | 久草热视频 | 一区二区网 | 免费色网| 欧美日韩在线视频一区 | 亚洲精品无| 国产98色在线 | 日韩 | 亚洲国产精品成人女人久久 | 亚洲激情小视频 | 色悠悠久久综合 | 国产色妞影院wwwxxx | 国产免费影院 | 日韩免费一区二区在线观看 | 久久久久99精品国产片 | 一级欧美黄 | 91成人亚洲| 999亚洲国产996395 | 国产精品激情在线观看 | 亚洲另类视频在线观看 | 日韩精品你懂的 | 国产精品入口麻豆www | 国产中文字幕视频在线观看 | 午夜精品一区二区三区在线视频 | 色婷婷激情四射 | 欧美va在线观看 | 国产中文字幕久久 | 毛片a级片| 午夜少妇一区二区三区 | 久久久久国产精品www | 丁香五月亚洲综合在线 | 国产精品免费在线视频 | www.亚洲精品视频 | 久久精品中文字幕 | 婷婷丁香激情综合 | 狠狠狠狠狠狠天天爱 | 久久免费资源 | 国产精品一区免费在线观看 | 中文字幕精品三级久久久 | 久久色视频 | av在线播放网址 | 夜夜躁狠狠躁日日躁视频黑人 | 免费看久久久 | 午夜精品久久久久久久久久久久 | 日韩 精品 一区 国产 麻豆 | 久久久国产一区二区三区四区小说 | 国产午夜精品一区 | 日韩伦理片hd | 日韩欧美高清免费 | 六月丁香色婷婷 | 天天操夜夜拍 | 狠狠狠狠狠干 | 亚洲亚洲精品在线观看 | 国产精品婷婷午夜在线观看 | 国产一区二区中文字幕 | 中文字幕日韩电影 | av.com在线| 亚洲人片在线观看 | 天天射天天干天天爽 | 亚洲六月丁香色婷婷综合久久 | 免费观看视频的网站 | 在线视频久久 | 最近中文字幕大全中文字幕免费 | 日本精品一区二区 | 97香蕉久久超级碰碰高清版 | 一区二区三区在线免费观看视频 | 久久精品视频在线看 | 精品中文字幕视频 | 97国产一区二区 | 99精品在线视频观看 | 欧美日本国产在线观看 | 久久人人精| 精品国产一区二区三区日日嗨 | 黄色成人在线网站 | 久久综合色天天久久综合图片 | 日韩欧美一区二区三区在线 | 久久极品 | 日本韩国精品一区二区在线观看 | 成人动漫视频在线 | 日韩精品短视频 | 欧美色婷婷| 国产尤物一区二区三区 | 色综合中文字幕 | 亚洲在线不卡 | 高清在线一区二区 | 中文字幕资源站 | 西西大胆啪啪 | 特片网久久 | 天天操天天添天天吹 | 欧美性生活小视频 | 天天操天天干天天操天天干 | 国产一区二区在线免费观看 | 成人av地址 | 99高清视频有精品视频 | 又大又硬又黄又爽视频在线观看 | 91最新中文字幕 | 一区二区视频在线播放 | 精品久久久免费视频 | 久久情网 | 天天干天天综合 | 成人免费共享视频 | 国产午夜在线观看视频 | 麻豆免费视频 | 99视频在线观看免费 | 国产96在线 | 三级在线视频观看 | 国产精品情侣视频 | 黄色大全免费网站 | 久久精品国产一区二区 | 999成人网| 超碰av在线免费观看 | 日韩有码专区 | 婷婷色网 | 国产自在线 | 亚洲激情综合 | 久久国产精品免费观看 | 国产欧美精品一区二区三区 | 日韩中文字幕免费视频 | 国产91丝袜在线播放动漫 | 亚洲综合色站 | 综合网婷婷 | 亚洲国产丝袜在线观看 | 天天天天天天天天操 | 国产亚洲精品无 | 91精品国产欧美一区二区 | 丁香花在线观看视频在线 | 在线观看一区二区精品 | 顶级bbw搡bbbb搡bbbb | 国产亚洲精品久久久久久移动网络 | 午夜在线观看影院 | 97精品国自产拍在线观看 | 国产一区二区精品久久91 | 色综合久久88色综合天天6 | 制服丝袜欧美 | 国产欧美综合在线观看 | 国产美女视频黄a视频免费 久久综合九色欧美综合狠狠 | 天天操天天色天天 | 在线观看麻豆av | 欧美日韩亚洲一 | 摸阴视频 | 国产三级久久久 | 欧美性脚交 | 免费看一级特黄a大片 | 中文字幕中文中文字幕 | 国产日韩欧美视频在线观看 | av在线免费观看黄 | 国产精品久久人 | 久久久黄色免费网站 | 色视频在线免费观看 | 玖草在线观看 | 久久久久久不卡 | 亚洲激情一区二区三区 | 四虎永久网站 | 91麻豆精品国产91久久久久 | 久久国产电影 | 国内精品久久久 | www.黄色片网站 | 日韩99热 | 久久久精品 | 日韩免费一级a毛片在线播放一级 | 韩日精品视频 | 亚洲国产黄色片 | 日韩中文在线视频 | av电影中文字幕在线观看 | 亚洲视频免费在线观看 | 一区三区在线欧 | 日韩天堂在线观看 | 国产拍在线 | 黄色成人av | 欧美日韩二区在线 | 中文字幕中文字幕在线中文字幕三区 | 在线成人一区二区 | 91视频在线自拍 | 日本系列中文字幕 | 五月婷婷久草 | 色姑娘综合网 | 高清在线观看av | 在线成人免费电影 | 三级a毛片 | 天天色宗合 | 美女免费黄网站 | 国产精品国产毛片 | 日日爱视频 | 欧美一二三视频 | 中文字幕成人在线观看 | 天天干天天操天天射 | 久久国产综合视频 | 黄色h在线观看 | 欧美一区二视频在线免费观看 | 欧美精品亚洲精品 | 日本精品久久久久影院 | 美女黄久久| 欧美日韩aaaa | 91亚洲国产成人 | 亚洲一片黄| 久久精品99国产精品 | 成年人免费在线 | 免费看黄色毛片 | 中文字幕五区 | 岛国av在线免费 | 国产伦精品一区二区三区… | 在线免费观看麻豆 | 日本成人免费在线观看 | 天天要夜夜操 | 午夜色影院| 国产精品麻豆欧美日韩ww | 91av视频观看 | 欧美日韩高清国产 | 一级黄色网址 | 免费观看特级毛片 | 欧美91成人网 | 国产一区免费看 | 444av| 黄在线| 日韩成人中文字幕 | 五月天视频网 | 久草在线中文888 | 一区二三国产 | 欧美精品首页 | 久久欧美精品 | 久久久在线 | 麻豆国产露脸在线观看 | 97视频一区| 99久久久免费视频 | 99精品国产视频 | 黄色免费看片网站 | 久久久国产毛片 | 亚洲激情小视频 | 久久人人爽人人爽人人片av软件 | 久久不卡国产精品一区二区 | 亚洲黄色影院 | 久久草草热国产精品直播 | 日本精品视频一区 | www.天天色.com| 97超碰免费 | www.在线观看视频 | 亚洲精品无 | 香蕉色综合| 精品国产乱码一区二 | 日韩电影在线观看中文字幕 | 国产一级视频免费看 | 亚洲国产日韩一区 | 国内精品久久久久影院男同志 | 亚洲视屏在线播放 | 狠狠色网| 99免费在线观看视频 | 操操色 | 色香天天 | 国产在线一线 | 免费看网站在线 | 国产精品成人久久久久 | 国产xvideos免费视频播放 | 99精品国产一区二区三区不卡 | 中文在线最新版天堂 | 超级碰碰免费视频 | 少妇bbw搡bbbb搡bbbb | 国产黄a三级 | 免费高清看电视网站 | 午夜av色 | a√国产免费a | 亚洲视频在线看 | 美女视频黄免费的 | 欧美日韩在线免费观看视频 | 日韩在线 | 欧美地下肉体性派对 | 黄色1级大片 | 久 久久影院 | 91九色视频国产 | 午夜精品久久久久久久久久久 | 日韩国产精品久久久久久亚洲 | 人人草人 | 国产成人黄色在线 | 国产精品美女久久久久久久久 | 久久五月婷婷丁香社区 | 麻豆91小视频 | 日韩精品aaa | 91香蕉视频在线 | 色com网| 久久,天天综合 | 午夜婷婷网|