內存結構
堆:由程序員手動分配和釋放,完全不同于數據結構中的堆,分配方式類似鏈表。由malloc(c語言)或new(c++)來分配,free(c語言)和delete(c++)釋放。若程序員不釋放,程序結束后由系統釋放。堆由低向高生長。空間大 靈活性大 速度慢。
棧:由編譯器自動分配和釋放的,存放函數的參數值,局部變量的值。操作方式類似數據結構中的棧。由高向低生長。空間小,靈活性差
全局(靜態)存儲區:存放全局變量和靜態變量。包括data段(全局初始化區)與BSS段(全局未初始化區)其中 初始化的全局變量和靜態變量存放在data段,未初始化的全局變量和未初始化的靜態變量存放在BSS段。程序結束后由系統釋放。其中BSS段的特點是,在程序執行之前BSS段會自動清0.隨意,未初始化的全局變量與靜態變量在程序執行之前已經成0了。
文字常量區:常量字符串就是放在這里的。程序結束后由系統釋放。
程序代碼區:存放函數體的二進制代碼。
常量在C++里的定義就是一個top-level const加上對象類型,常量定義必須初始化。對于局部對象,常量存放在棧區,對于全局對象,常量存放在全局/靜態存儲區。對于字面值常量,常量存放在常量存儲區。
Public和private的區別
public和private是類里的關鍵字,用于規定類內數據或者成員函數的訪問權限。private類型的數據或者函數,只能在相應的類內被訪問,而public類型的數據或者函數被訪問的權限比較寬,還可以在其它類或者其它函數中被訪問。
可以通過友元函數的方式在其他類中訪問私有成員函數。
構造函數可以私有嗎?
可以。
私有構造函數意味著只能在自身內部創建實例,加上static可以保證改類只有一個實例。
這種方式常用語單例模式。
什么是多態,什么是動態綁定。
多態是指同一個函數可以根據對象的不同而采用多種不同的行為方式.與之相對應是靜態綁定,即在函數編譯的時候決定要調用的函數。動態綁定,只有當程序運行的時候才能根據具體的對象來調用相應的函數。
多態存在的三個必要條件
一、要有繼承;
二、要有重寫;
三、父類引用指向子類對象。
回調函數和回調機制
⑴定義一個回調函數;
⑵提供函數實現的一方在初始化的時候,將回調函數的函數指針注冊給調用者;
⑶當特定的事件或條件發生的時候,調用者使用函數指針調用回調函數對事件進行處理。
被調用的函數。
Static 關鍵字的作用
1. 全局靜態變量
在全局變量前加上關鍵字static,全局變量就定義成一個全局靜態變量.
靜態存儲區,在整個程序運行期間一直存在。
初始化:未經初始化的全局靜態變量會被自動初始化為0(自動對象的值是任意的,除非他被顯式初始化);
作用域:全局靜態變量在聲明他的文件之外是不可見的,準確地說是從定義之處開始,到文件結尾。
2. ?局部靜態變量
在局部變量之前加上關鍵字static,局部變量就成為一個局部靜態變量。
內存中的位置:靜態存儲區
初始化:未經初始化的全局靜態變量會被自動初始化為0(自動對象的值是任意的,除非他被顯式初始化);
作用域:作用域仍為局部作用域,當定義它的函數或者語句塊結束的時候,作用域結束。但是當局部靜態變量離開作用域后,并沒有銷毀,而是仍然駐留在內存當中,只不過我們不能再對它進行訪問,直到該函數再次被調用,并且值不變;
3. 靜態函數
在函數返回類型前加static,函數就定義為靜態函數。函數的定義和聲明在默認情況下都是extern的,但靜態函數只是在聲明他的文件當中可見,不能被其他文件所用。
函數的實現使用static修飾,那么這個函數只可在本cpp內使用,不會同其他cpp中的同名函數引起沖突;
warning:不要再頭文件中聲明static的全局函數,不要在cpp內聲明非static的全局函數,如果你要在多個cpp中復用該函數,就把它的聲明提到頭文件里去,否則cpp內部聲明需加上static修飾;
4. 類的靜態成員
在類中,靜態成員可以實現多個對象之間的數據共享,并且使用靜態數據成員還不會破壞隱藏的原則,即保證了安全性。因此,靜態成員是類的所有對象中共享的成員,而不是某個對象的成員。對多個對象來說,靜態數據成員只存儲一處,供所有對象共用
5. 類的靜態函數
靜態成員函數和靜態數據成員一樣,它們都屬于類的靜態成員,它們都不是對象成員。因此,對靜態成員的引用不需要用對象名。
在靜態成員函數的實現中不能直接引用類中說明的非靜態成員,可以引用類中說明的靜態成員(這點非常重要)。如果靜態成員函數中要引用非靜態成員時,可通過對象來引用。從中可看出,調用靜態成員函數使用如下格式:<類名>::<靜態成員函數名>(<參數表>);
?說一下C++和C的區別
設計思想上:
C++是面向對象的語言,而C是面向過程的結構化編程語言
語法上:
C++具有封裝、繼承和多態三種特性
C++相比C,增加多許多類型安全的功能,比如強制類型轉換、
C++支持范式編程,比如模板類、函數模板等
?說一下C++中static關鍵字的作用
對于函數定義和代碼塊之外的變量聲明,static修改標識符的鏈接屬性,由默認的external變為internal,作用域和存儲類型不改變,這些符號只能在聲明它們的源文件中訪問。
對于代碼塊內部的變量聲明,static修改標識符的存儲類型,由自動變量改為靜態變量,作用域和鏈接屬性不變。這種變量在程序執行之前就創建,在程序執行的整個周期都存在。
對于被static修飾的普通函數,其只能在定義它的源文件中使用,不能在其他源文件中被引用
對于被static修飾的類成員變量和成員函數,它們是屬于類的,而不是某個對象,所有對象共享一個靜態成員。靜態成員通過<類名>::<靜態成員>來使用。
?說一說c++中四種cast轉換
C++中四種類型轉換是:static_cast, dynamic_cast, const_cast, reinterpret_cast
??? static_cast<double>(a) / static_cast<double>(b);
1、const_cast
用于將const變量轉為非const
2、static_cast
??(1)用于類層次結構中基類和派生類之間指針或引用的轉換
? ??? 進行上行轉換(把派生類的指針或引用轉換成基類表示)是安全的
? ??? 進行下行轉換(把基類的指針或引用轉換為派生類表示),由于沒有動態類型檢查,所以是不安全的
? ? (2)用于基本數據類型之間的轉換,如把int轉換成char。這種轉換的安全也要開發人員來保證
? ? (3)把空指針轉換成目標類型的空指針
? ? (4)把任何類型的表達式轉換為void類型
3、dynamic_cast
用于動態類型轉換。只能用于含有虛函數的類,用于類層次間的向上和向下轉化。只能轉指針或引用。向下轉化時,如果是非法的對于指針返回NULL,對于引用拋異常。要深入了解內部轉換的原理。
向上轉換:指的是子類向基類的轉換
向下轉換:指的是基類向子類的轉換
它通過判斷在執行到該語句的時候變量的運行時類型和要轉換的類型是否相同來判斷是否能夠進行向下轉換。
4、reinterpret_cast
幾乎什么都可以轉,比如將int轉指針,可能會出問題,盡量少用;
5、為什么不使用C的強制轉換?
C的強制轉換表面上看起來功能強大什么都能轉,但是轉化不夠明確,不能進行錯誤檢查,容易出錯。
請說一下C/C++ 中指針和引用的區別?
1.指針有自己的一塊空間,而引用只是一個別名;
2.使用sizeof看一個指針的大小是4,而引用則是被引用對象的大小;
3.指針可以被初始化為NULL,而引用必須被初始化且必須是一個已有對象 的引用;
4.作為參數傳遞時,指針需要被解引用才可以對對象進行操作,而直接對引 用的修改都會改變引用所指向的對象;
5.可以有const指針,但是沒有const引用;
6.指針在使用中可以指向其它對象,但是引用只能是一個對象的引用,不能 被改變;
7.指針可以有多級指針(**p),而引用至于一級;
8.指針和引用使用++運算符的意義不一樣;
9.如果返回動態內存分配的對象或者內存,必須使用指針,引用可能引起內存泄露。
1、引用:
C++是C語言的繼承,它可進行過程化程序設計,又可以進行以抽象數據類型為特點的基于對象的程序設計,還可以進行以繼承和多態為特點的面向對象的程序設計。引用就是C++對C語言的重要擴充。引用就是某一變量的一個別名,對引用的操作與對變量直接操作完全一樣。引用的聲明方法:類型標識符 &引用名=目標變量名;引用引入了對象的一個同義詞。定義引用的表示方法與定義指針相似,只是用&代替了*。
2、指針:
指針利用地址,它的值直接指向存在電腦存儲器中另一個地方的值。由于通過地址能找到所需的變量單元,可以說,地址指向該變量單元。因此,將地址形象化的稱為“指針”。意思是通過它能找到以它為地址的內存單元。
請你說一下你理解的c++中的smart pointer四個智能指針shared_ptr,unique_ptr,weak_ptr,auto_ptr
智能指針的作用是管理一個指針,因為存在以下這種情況:申請的空間在函數結束時忘記釋放,造成內存泄漏。使用智能指針可以很大程度上的避免這個問題,因為智能指針就是一個類,當超出了類的作用域是,類會自動調用析構函數,析構函數會自動釋放資源。所以智能指針的作用原理就是在函數結束時自動釋放內存空間,不需要手動釋放內存空間。
1、auto_ptr(c++98的方案,cpp11已經拋棄)
采用所有權模式。
auto_ptr< string> p1 (new string ("I reigned lonely as a cloud.”));
auto_ptr<string> p2;
p2 = p1; //auto_ptr不會報錯.
此時不會報錯,p2剝奪了p1的所有權,但是當程序運行時訪問p1將會報錯。所以auto_ptr的缺點是:存在潛在的內存崩潰問題!
2、unique_ptr(替換auto_ptr)
unique_ptr實現獨占式擁有或嚴格擁有概念,保證同一時間內只有一個智能指針可以指向該對象。它對于避免資源泄露(例如“以new創建對象后因為發生異常而忘記調用delete”)特別有用。
采用所有權模式,還是上面那個例子
unique_ptr<string> p3 (new string ("auto")); ??????????//#4
unique_ptr<string> p4; ??????????????????????????//#5
p4 = p3;//此時會報錯!!
編譯器認為p4=p3非法,避免了p3不再指向有效數據的問題。因此,unique_ptr比auto_ptr更安全。
另外unique_ptr還有更聰明的地方:當程序試圖將一個 unique_ptr 賦值給另一個時,如果源 unique_ptr 是個臨時右值,編譯器允許這么做;如果源 unique_ptr 將存在一段時間,編譯器將禁止這么做,比如:
unique_ptr<string> pu1(new string ("hello world"));
unique_ptr<string> pu2;
pu2 = pu1; ?????????????????????????????????????????// #1 not allowed
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string ("You")); ??????????// #2 allowed
其中#1留下懸掛的unique_ptr(pu1),這可能導致危害。而#2不會留下懸掛的unique_ptr,因為它調用 unique_ptr 的構造函數,該構造函數創建的臨時對象在其所有權讓給 pu3 后就會被銷毀。這種隨情況而已的行為表明,unique_ptr 優于允許兩種賦值的auto_ptr 。
注:如果確實想執行類似與#1的操作,要安全的重用這種指針,可給它賦新值。C++有一個標準庫函數std::move(),讓你能夠將一個unique_ptr賦給另一個。例如:
unique_ptr<string> ps1, ps2;
ps1 = demo("hello");
ps2 = move(ps1);
ps1 = demo("alexia");
cout << *ps2 << *ps1 << endl;
3、shared_ptr
shared_ptr實現共享式擁有概念。多個智能指針可以指向相同對象,該對象和其相關資源會在“最后一個引用被銷毀”時候釋放。從名字share就可以看出了資源可以被多個指針共享,它使用計數機制來表明資源被幾個指針共享。可以通過成員函數use_count()來查看資源的所有者個數。除了可以通過new來構造,還可以通過傳入auto_ptr, unique_ptr,weak_ptr來構造。當我們調用release()時,當前指針會釋放資源所有權,計數減一。當計數等于0時,資源會被釋放。
shared_ptr 是為了解決 auto_ptr 在對象所有權上的局限性(auto_ptr 是獨占的), 在使用引用計數的機制上提供了可以共享所有權的智能指針。
成員函數:
use_count 返回引用計數的個數
unique 返回是否是獨占所有權( use_count 為 1)
swap 交換兩個 shared_ptr 對象(即交換所擁有的對象)
reset 放棄內部對象的所有權或擁有對象的變更, 會引起原有對象的引用計數的減少
get 返回內部對象(指針), 由于已經重載了()方法, 因此和直接使用對象是一樣的.如 shared_ptr<int> sp(new int(1)); sp 與 sp.get()是等價的
4、weak_ptr
weak_ptr 是一種不控制對象生命周期的智能指針, 它指向一個 shared_ptr 管理的對象. 進行該對象的內存管理的是那個強引用的 shared_ptr. weak_ptr只是提供了對管理對象的一個訪問手段。weak_ptr 設計的目的是為配合 shared_ptr 而引入的一種智能指針來協助 shared_ptr 工作, 它只可以從一個 shared_ptr 或另一個 weak_ptr 對象構造, 它的構造和析構不會引起引用記數的增加或減少。weak_ptr是用來解決shared_ptr相互引用時的死鎖問題,(避免成環)如果說兩個shared_ptr相互引用,那么這兩個指針的引用計數永遠不可能下降為0,資源永遠不會釋放。它是對對象的一種弱引用,不會增加對象的引用計數,和shared_ptr之間可以相互轉化,shared_ptr可以直接賦值給它,它可以通過調用lock函數來獲得shared_ptr。
介紹一下智能指針
智能指針主要用于管理在堆上分配的內存,它將普通的指針封裝為一個棧對象。當棧對象的生存周期結束后,會在析構函數中釋放掉申請的內存,從而防止內存泄漏。C++ 11中最常用的智能指針類型為shared_ptr,它采用引用計數的方法,記錄當前內存資源被多少個智能指針引用。該引用計數的內存在堆上分配。當新增一個時引用計數加1,當過期時引用計數減一。只有引用計數為0時,智能指針才會自動釋放引用的內存資源。對shared_ptr進行初始化時不能將一個普通指針直接賦值給智能指針,因為一個是指針,一個是類。可以通過make_shared函數或者通過構造函數傳入普通指針。并可以通過get函數獲得普通指針。
智能指針內存泄漏情況
- auto_ptr 的擁有權被另一個指針剝奪,再次訪問原指針會導致崩潰(嚴格意義上不算內存泄漏)
- shared_ptr成環,形成死鎖,內存永遠無法釋放
虛函數表
一個類只有一個虛函數表存放在只讀數據段,。每個類的實例化對象有一個虛函數表指針指向虛函數表。
只要父類有虛函數表,不論子類有沒有虛函數,都有虛函數表。基類的虛函數表和子類的虛函數表不是同一個表
單繼承中父類一個虛函數表,子類一個虛函數表,如子類重寫父類虛函數,子類虛函數表相應內容會被覆蓋,指向子類函數。
多次單繼承中 子類在父類的基礎上進行修改。
多基繼承:有多少個基類類就有多少個虛函數表
1.子類虛函數會覆蓋每一個父類的每一個同名虛函數。
2.父類中沒有的虛函數而子類有,填入第一個虛函數表中,且父類指針是不能調用。
3.父類中有的虛函數而子類沒有,則不覆蓋。僅子類和該父類指針能調用。
4.如果子類有新的虛函數,那么就添加到第一個虛函數表的末尾。
Explicit的作用是
C++提供了關鍵字explicit,可以阻止不應該允許的經過轉換構造函數進行的隱式轉換的發生。聲明為explicit的構造函數不能在隱式轉換中使用。
Const關鍵字
int i = 0;
const int* p1 = &i;??? 指向的值不能改變,但是指針可以改變
int const* p2 = &i;?????? 指向的值不能改變,但是指針可以改變
int* const p3 = &i;???? 指針不能改變 ?,值可以改變
const int* const p4 = &i;? 都不能變
當?const
?常量作為參數傳入時,該常量一定需要是引用類型???
void setName(const string& name); 引用傳遞才能真正起到 const 的作用
void setName(const string name);? 值傳遞 傳進來是副本 不會造成更改
const對象只能訪問const成員函數,而非const對象可以訪問任意的成員函數,包括const成員函數;
const對象的成員是不能修改的,而通過指針維護的對象確實可以修改的;
const成員函數不可以修改對象的數據,不管對象是否具有const性質。編譯時以是否修改成員數據為依據進行檢查。
任何不會修改數據成員的函數都應該聲明為const類型。如果在編寫const成員函數時,不慎修改了數據成員,或者調用了其它非const成員函數,編譯器將指出錯誤。
delete 和 delete []的真正區別
- delete 對應 new delete[]對應new[]
- 對于簡單類型包括簡單類型數組,delete 與delete[]沒有區別。對于自定義類型數組,delete 只會刪除一個元素,delete 則會刪除所有元素。
野指針是什么
野指針就是指向一個已刪除的對象或者未申請訪問受限內存區域的指針
請你回答一下為什么析構函數必須是虛函數?為什么C++默認的析構函數不是虛函數?
將可能會被繼承的父類的析構函數設置為虛函數,可以保證當我們new一個子類,然后使用基類指針指向該子類對象,釋放基類指針時可以釋放掉子類的空間,防止內存泄漏。如果不設置基類指針只能訪問非繼承成員。
C++默認的析構函數不是虛函數是因為虛函數需要額外的虛函數表和虛表指針,占用額外的內存。而對于不會被繼承的類來說,其析構函數如果是虛函數,就會浪費內存。因此C++默認的析構函數不是虛函數,而是只有當需要當作父類時,設置為虛函數。
請你來說一下C++中析構函數的作用
析構函數與構造函數對應,當對象結束其生命周期,如對象所在的函數已調用完畢時,系統會自動執行析構函數。
析構函數名也應與類名相同,只是在函數名前面加一個位取反符~,例如~stud( ),以區別于構造函數。它不能帶任何參數,也沒有返回值(包括void類型)。只能有一個析構函數,不能重載。
如果用戶沒有編寫析構函數,編譯系統會自動生成一個缺省的析構函數(即使自定義了析構函數,編譯器也總是會為我們合成一個析構函數,并且如果自定義了析構函數,編譯器在執行時會先調用自定義的析構函數再調用合成的析構函數),它也不進行任何操作。所以許多簡單的類中沒有用顯式的析構函數。
如果一個類中有指針,且在使用的過程中動態的申請了內存,那么最好顯示構造析構函數在銷毀類之前,釋放掉申請的內存空間,避免內存泄漏。
類析構順序:1)派生類本身的析構函數;2)對象成員析構函數;3)基類析構函數。
請你來說一下靜態函數和虛函數的區別
靜態函數在編譯的時候就已經確定運行時機,虛函數在運行的時候動態綁定。虛函數因為用了虛函數表機制,調用的時候會增加一次內存開銷
以下四行代碼的區別是什么? const char * arr = "123"; char * brr = "123"; const char crr[] = "123"; char drr[] = "123";
const char * arr = "123";
//字符串123保存在常量區,const本來是修飾arr指向的值不能通過arr去修改,但是字符串“123”在常量區,本來就不能改變,所以加不加const效果都一樣
char * brr = "123";
//字符串123保存在常量區,這個arr指針指向的是同一個位置,同樣不能通過brr去修改"123"的值
const char crr[] = "123";
//這里123本來是在棧上的,但是編譯器可能會做某些優化,將其放到常量區
char drr[] = "123";
//字符串123保存在棧區,可以通過drr去修改
請你回答一下new/delete與malloc/free的區別是什么
首先,new/delete是C++的關鍵字,而malloc/free是C語言的庫函數,后者使用必須指明申請內存空間的大小,對于類類型的對象,后者不會調用構造函數和析構函數。
請你說說C++如何處理返回值?
生成一個臨時變量,把它的引用作為函數參數傳入函數內。
請你說一說select fork,wait,exec函數
父進程產生子進程使用fork拷貝出來一個父進程的副本,此時只拷貝了父進程的頁表,兩個進程都讀同一塊內存,當有進程寫的時候使用寫實拷貝機制分配內存,exec函數可以加載一個elf文件去替換父進程,從此父進程和子進程就可以運行不同的程序了。fork從父進程返回子進程的pid,從子進程返回0.調用了wait的父進程將會發生阻塞,直到有子進程狀態改變,執行成功返回0,錯誤返回-1。exec執行成功則子進程從新的程序開始運行,無返回值,執行失敗返回-1
請你來說一下map和set有什么區別,分別又是怎么實現的?
map和set都是C++的關聯容器,其底層實現都是紅黑樹(RB-Tree)。由于 map 和set所開放的各種操作接口,RB-tree 也都提供了,所以幾乎所有的 map 和set的操作行為,都只是轉調 RB-tree 的操作行為。
map和set區別在于:
(1)map中的元素是key-value(關鍵字—值)對:關鍵字起到索引的作用,值則表示與索引相關聯的數據;Set與之相對就是關鍵字的簡單集合,set中每個元素只包含一個關鍵字。
(2)set的迭代器是const的,不允許修改元素的值;map允許修改value,但不允許修改key。其原因是因為map和set是根據關鍵字排序來保證其有序性的,如果允許修改key的話,那么首先需要刪除該鍵,然后調節平衡,再插入修改后的鍵值,調節平衡,如此一來,嚴重破壞了map和set的結構,導致iterator失效,不知道應該指向改變前的位置,還是指向改變后的位置。所以STL中將set的迭代器設置成const,不允許修改迭代器的值;而map的迭代器則不允許修改key值,允許修改value值。
(3)map支持下標操作,set不支持下標操作。map可以用key做下標,map的下標運算符[ ]將關鍵碼作為下標去執行查找,如果關鍵碼不存在,則插入一個具有該關鍵碼和mapped_type類型默認值的元素至map中,因此下標運算符[ ]在map應用中需要慎用,const_map不能用,只希望確定某一個關鍵值是否存在而不希望插入元素時也不應該使用,mapped_type類型沒有默認值也不應該使用。如果find能解決需要,盡可能用find。
unordered map底層結構是哈希表
?請你說一說vector和list的區別,應用,越詳細越好
1、概念:
1)Vector
連續存儲的容器,動態數組,在堆上分配空間
底層實現:數組
兩倍容量增長:
vector 增加(插入)新元素時,如果未超過當時的容量,則還有剩余空間,那么直接添加到最后(插入指定位置),然后調整迭代器。
如果沒有剩余空間了,則會重新配置原有元素個數的兩倍空間,然后將原空間元素通過復制的方式初始化新空間,再向新空間增加元素,最后析構并釋放原空間,之前的迭代器會失效。
性能:
訪問:O(1)
插入:在最后插入(空間夠):很快
在最后插入(空間不夠):需要內存申請和釋放,以及對之前數據進行拷貝。
在中間插入(空間夠):內存拷貝
在中間插入(空間不夠):需要內存申請和釋放,以及對之前數據進行拷貝。
刪除:在最后刪除:很快
在中間刪除:內存拷貝
適用場景:經常隨機訪問,且不經常對非尾節點進行插入刪除。
2、List
動態鏈表,在堆上分配空間,每插入一個元數都會分配空間,每刪除一個元素都會釋放空間。
底層:雙向鏈表
性能:
訪問:隨機訪問性能很差,只能快速訪問頭尾節點。
插入:很快,一般是常數開銷
刪除:很快,一般是常數開銷
適用場景:經常插入刪除大量數據
2、區別:
1)vector底層實現是數組;list是雙向 鏈表。
2)vector支持隨機訪問,list不支持。
3)vector是順序內存,list不是。
4)vector在中間節點進行插入刪除會導致內存拷貝,list不會。
5)vector一次性分配好內存,不夠時才進行2倍擴容;list每次插入新節點都會進行內存申請。
6)vector隨機訪問性能好,插入刪除性能差;list隨機訪問性能差,插入刪除性能好。
3、應用
vector擁有一段連續的內存空間,因此支持隨機訪問,如果需要高效的隨即訪問,而不在乎插入和刪除的效率,使用vector。
list擁有一段不連續的內存空間,如果需要高效的插入和刪除,而不關心隨機訪問,則應使用list。
請你來說一下STL中迭代器的作用,有指針為何還要迭代器
1、迭代器
Iterator(迭代器)模式又稱Cursor(游標)模式,用于提供一種方法順序訪問一個聚合對象中各個元素, 而又不需暴露該對象的內部表示。或者這樣說可能更容易理解:Iterator模式是運用于聚合對象的一種模式,通過運用該模式,使得我們可以在不知道對象內部表示的情況下,按照一定順序(由iterator提供的方法)訪問聚合對象中的各個元素。
由于Iterator模式的以上特性:與聚合對象耦合,在一定程度上限制了它的廣泛運用,一般僅用于底層聚合支持類,如STL的list、vector、stack等容器類及ostream_iterator等擴展iterator。
2、迭代器和指針的區別
迭代器不是指針,是類模板,表現的像指針。他只是模擬了指針的一些功能,通過重載了指針的一些操作符,->、*、++、--等。迭代器封裝了指針,是一個“可遍歷STL( Standard Template Library)容器內全部或部分元素”的對象,?本質是封裝了原生指針,是指針概念的一種提升(lift),提供了比指針更高級的行為,相當于一種智能指針,他可以根據不同類型的數據結構來實現不同的++,--等操作。
迭代器返回的是對象引用而不是對象的值,所以cout只能輸出迭代器使用*取值后的值而不能直接輸出其自身。
3、迭代器產生原因
Iterator類的訪問方式就是把不同集合類的訪問邏輯抽象出來,使得不用暴露集合內部的結構而達到循環遍歷集合的效果。
請你回答一下STL里resize和reserve的區別
resize():改變當前容器內含有元素的數量(size()),eg: vector<int>v; v.resize(len);v的size變為len,如果原來v的size小于len,那么容器新增(len-size)個元素,元素的值為默認為0.當v.push_back(3);之后,則是3是放在了v的末尾,即下標為len,此時容器是size為len+1;
reserve():改變當前容器的最大容量(capacity),它不會生成元素,只是確定這個容器允許放入多少對象,如果reserve(len)的值大于當前的capacity(),那么會重新分配一塊能存len個對象的空間,然后把之前v.size()個對象通過copy construtor復制過來,銷毀之前的內存;
左右值
在C++11中可以取地址的、有名字的就是左值,反之,不能取地址的、沒有名字的就是右值(將亡值或純右值)。
const左值引用會延長右值的生命周期。