C++基礎要點摘記

1.定義是創建與符號關聯的實體。聲明是讓符號爲程序所知

聲明變量的同時也就完成了變量的定義,只有聲明外部全局變量的情況是例外當一個文件想要使用其他文件定義的全局變量,則必須聲明

函數和變量的聲明不會分配內存, 但是定義會分配相應的內存空間變量名就是對相應的內存單元的命名

函數和變量的聲明可以有很多次, 但是定義最多隻能有一次

2.將static全局變量寫在頭文件中,所有頭文件的操作函數都會共享這個變量但如果是在源文件中去操作這個靜態全局變量,則這個靜態全局變量只能在當前文件有效,但是在另外一個文件訪問此靜態變量,會是該變量初始的默認值,不會是其他文件中修改的值,雖然它們有相同的初始內容,但是存儲的物理地址並不一樣

一般用法:static修飾全局變量或函數並未改變其存儲位置生命週期,而是改變了其作用域好處如下:(1)不會被其他文件所訪問2其他文件中可以使用相同名字的變量,不會發生衝突3全局函數變成靜態函數

static修飾局部變量一般用在函數中,生命週期從程序開始到結束,作用域是函數的編譯單元,每次調用函數操作的是同一個靜態變量。

靜態成員變量不可以在類內初始化,因爲這樣會爲每個對象創一個,不可以在頭文件的類外初始化,因爲這樣會爲每個include了它的源文件創一個(參考此點開頭的說明)。在類外初始化時不加static。但是可以用const修飾static數據成員在類內初始化因爲常數據成員

靜態成員函數的實現中不能直接引用類中說明的非靜態成員,可以引用類中說明的靜態成員。因爲靜態成員函數參數不含this指針。

const成員變量可以在類聲明中初始化(類似普通成員變量),或者只能在構造函數的初始化列表初始化,因爲const定義的時候必須初始化,不能進行賦值

static const成員變量可以在類聲明中初始化,也可以用類中的無名的private enum替換它定義int的作用。

3. 重載、覆寫(重寫override)、隱藏、(從名稱空間上作答)實例化=生成對象、抽象類一般用作聲明函數接口供繼承

4. struct和union都是由多個不同的數據類型成員組成,union中只存放了一個被選中的成員,所有成員共用一段內存空間,對於union的成員賦值,將會覆蓋其它成員而struct的所有成員都佔有自己的內存空間一個struct變量的總長度等於所有成員長度之和Union變量的長度等於最長的成員的長度。

類、Struct的對齊首先根據結構體內部成員的對齊值系統指定對齊值得到結構體的自身對齊值,取較小的以此對齊值進行數據放置,與內存補齊連續的數據可以和並再補齊如果一個數據大小不足對齊值的整數倍則放入內存中的時候對它進行補齊,使它佔有對齊值整數倍的空間,使得每個數據的開頭地址都是補齊值的整數倍

1.數據類型自身的對齊值    2.結構體或者類的自身對齊值:其成員中自身對齊值最大的那個值。

3.指定對齊值:#pragma pack(n),n=1,2,4,8,16改變系統的對齊

4.數據成員、結構體和類的有效對齊值:自身對齊值和指定對齊值中小的那個值。

5. 多態動態多態(運行期多態):程序在運行時才決定調用哪個函數(函數地址在運行時而不是編譯時根據引用或指針指向的對象與函數符號綁定即動態綁定,動態聯編),一個接口,多種方法實現了父子類的接口複用封裝通過struct\class和三個訪問控制權來完成,爲了保護內部數據成員,隔離外部干擾,也使代碼模塊化方便複用繼承可以擴展已存在的代碼,爲了代碼靜態多態(編譯期多態)是通過重載和模板技術實現

動態多態由虛函數和繼承,虛函數主要通過虛函數表(V-Table)實現。如果一個類包含虛函數,那麼就會擁有一張虛函數表在編譯器就建立,虛函數表存儲的每一項是一個虛函數的入口地址。對象不包含虛函數表,只有隱藏成員--虛指針(在運行期生成,指向虛表,方便在運行期根據對象查詢到底執行哪個函數,即動態綁定虛指針存在於對象實例地址的最前面,保證虛函數表有最高的性能),派生類會生成一個兼容基類的虛函數表。

 

 

 

繼承的虛函數和自己創建的虛函數,可以看到虛表放在基類對象虛表的位置上。

具體看https://www.cnblogs.com/LUO77/p/5771237.html

www.cnblogs.com/fanzhidongyzby/archive/2013/01/14/2859064詳對象模型,有adjustor

這些圖都是編譯器可以查看的內存佈局

多(重)繼承:派生類繼承多個基類,爲每個基類(顯式或隱式地默認私有)指定了訪問級別派生類的對象包含每個基類的對象派生類構造函數的初始化列表(因爲要先構造出基類對象)調用基類構造函數(否則將調用基類的默認構造函數,然後可以在函數體內對基類對象部分進行初始化,或者不初始化),基類構造函數按照基類構造函數在類派生聲明(而不是初始化列表)中的出現次序調用析構總是按構造函數運行的逆序調用析構函數

基類的析構函數最好寫成virtual,否則用基類指針對子類對象delete的時候,無法銷燬子類對象資源造成資源泄露(effective C++的建議是在類有至少一個virtual函數時,給類聲明一個virtual析構,還說即使沒有其他virtual,如果會被繼承,還是要virtual析構)有時候想讓一個類成爲抽象類但苦於沒有哪個函數好作爲純虛函數,可以讓析構成爲純虛而且要定義它,否則鏈接器會抱怨,既解決了子類對象不銷燬問題,又使類成爲抽象類。

虛基類(虛繼承)讓子類對象中擁有一個共享基類對象副本(如在菱形繼承中,子類的父類都繼承了同一個基類(公共基類),導致子類對象中可能出現多個基類對象),解決二義性,即解決了公共基類的多份拷貝的問題

子類對象中包括各基類對象副本,其中每個基類(MyClassA和MyClassB)和公共基類(MyClass)對象有自己的虛函數表指針,每個虛擬繼承了MyClass的父類對象還需要記錄一個指向虛基類表vbtable的指針vbptr

注:最下面兩層是虛基類,虛基類表第一項記錄着當前子對象相對虛基類表指針的偏移第二項是虛基類對象地址(即虛基類的虛指針)相對於虛基類表指針的偏移這樣一來每個子類對象都能找到那唯一的虛基類

用法istreamostream類對它們的基類ios進行虛繼承使基類成爲虛基類,如果其他類如iostream同時繼承它們兩個,則派生類對象中僅有一個ios對象副本。繼承可以寫成:virtual public或者:public virtual。

6.拷貝/賦值構造:如果要爲派生類編寫拷貝構造(/賦值)函數,則對於拷貝構造來說需要調用基類相應拷貝構造函數併爲其傳遞參數(在初始化列表中),否則將調用基類的默認構造(即無參數構造),使基類對象部分只有默認賦值;對於拷貝賦值來說需要在拷貝賦值函數體中調用A::operator=(B);將參數B的基類對象部分賦給*this。

深拷貝與淺拷貝

淺拷貝:默認拷貝構造(/賦值)函數只是完成了對象之間的值的拷貝,也就是把對象裏成員的值完全複製給另一個對象,如A=B。這時,如果B中有一個成員變量指針已經申請了堆內存,那A中的那個成員變量也指向同一塊內存這就出現了問題:當B把內存釋放了(如:析構),這時A內的指針就是野指針了,出現運行錯誤。

深拷貝:自定義複製構造函數需要對象成員指向的資源也進行複製,即A有5個空間,B也應該申請5個空間,而不是指向A的5個空間。

7.各個排序算法的時間複雜度和穩定性,快排的原理。

 

冒泡排序改進1:在某次遍歷中如果沒有數據交換,說明整個數組已經有序。因此通過設置標誌位來記錄此次遍歷有無數據交換就可以判斷是否要繼續循環(原做法是重複n次)

冒泡排序改進2:記錄某次遍歷時最後發生數據交換的位置,這個位置之後的數據顯然已經有序了。因此通過記錄最後發生數據交換的位置就可以確定下次循環的範圍了。

快排優化:①STL裏用1of 3,一般用隨機基準點。②有大量重複元素的數組中(考慮一個全是重複元素的數組),可能會把數組分割爲長度差距很大的兩部分,又變成了O(n^2),所以用雙路快排,即使碰到基準元素也停下來交換(與快排相反)③三路快排又進行了優化,只需要對小於和大於基準點的範圍進行排序即可,中間是等於基準點的,代碼:

http://www.javashuo.com/article/p-zjjkrazu-hh.html

選擇排序相對於插入排序來說有元素交換,所以不穩定,如[5,8,5,2,9]只有它比較次數與初始排列無關。

調整法建時間複雜度是O(n)(推起來很複雜),插入法是O(nlogn)(和紅黑樹一樣)。爲什麼不穩定:交換堆頭尾元素(pop_heap其實可以直接根與最後一個節點交換,再下沉即可。而STL先拿出最後一個節點,將根節點放進最後的洞,讓其他下面的節點先上溯,最後一個空洞放原最後一個葉節點,再讓它上溯,這樣變麻煩了,不知爲何)

爲什麼STL不一開始就用heapsort?因爲它每次比較和交換都是將一個元素變得有序,所以次數比快速排序多,常數因子比快速排序快排每一次交換都會使數據更加有序;元素交換的位置相對遠,對緩存不友好;快排發生惡化的情況很少發生。

introsort對一段排序前先判斷遞歸深度是否大於logn惡化,如惡化則heapsort並返回,再判斷序列長度是否小於16,如小於則放置不排並返回,再進行此段的快排,對左右兩端遞歸此過程,全部返回後再對整個序列進行插入排序,因爲基本有序的序列用插入性能比較好。

歸併在兩個有序序列合成一個的環節,要設置前面序列元素等於後面的時候先放前面的才能實現穩定。

基數排序第一步根據數字的個位放置(分配)到每個桶裏,然後將數字按照桶的次序輸出(串起來);然後根據十位分桶,繼續按照桶的次序再串起來。直至所有位被完,所有數字已經有序。

8.各容器的特點

 

allacator「內存配置器」,通常它是一個類,負責內存管理STL容器使用擁有層次結構的、高效率的STL配置器類,類中有函數負責內存的分配釋放。考慮到小型區塊可能造成內存碎片問題,設計了雙層配置器,第一級配置器直接malloc和free,第二級配置器利用內存池視申請大小採用不同策略,超128字節則用一級,小於則用二級的內存池,維護16個鏈表負責16種大小內存塊8-128的存儲與分配,取決於某個_USE_MALLOC預定義宏是否被定義(默認未被定義,用一級)來辨別目前是用哪種配置器,它定不定義其實是啓動一個條件編譯,給一級或二級一個typedef成alloc。而STL又給配置器包裝了一個simple_alloc接口,接口中對配置器中的allocate和deallocate函數進行了包裝,容器都是用這個alloc作爲Alloc模板參數的缺省參數值,然後用它作爲simple_alloc的模板參數實例化接口。(第二配置器內存池不足就找第一配置器分配,第一配置器內存不足也是類似new的set_new_handler設置一個異常處理函數,函數也沒辦法就bad_alloc異常。

而對象的構造是全局函數construct(調用調用placement new的new關鍵字)析構是全局函數destroy(調用類型T的析構函數)

vector採用線性空間存儲數據。如果空間不夠,則另外分配新的兩倍大小的空間,然後把舊空間釋放掉。不適合push_front,不適合中間插入,適合隨機訪問。需要高效的隨機存取,而不在乎插入和刪除的效率,使用vector。

vector的reserve只改變capacity。vector.size()表示的這個vector裏容納了多少個元素,capacity表示容器在必須分配存儲空間之前可以存儲的元素總數(即vector能夠容納多少元素),vector的capacity是2倍增長的。如果vector的大小不夠了,比如現在的capacity是4,插入到第五個元素的時候,發現不夠了,此時會給他重新分配8個空間,把原來的數據及新的數據複製到這個新分配的空間裏所以會有迭代器失效(它的迭代器就是普通指針)的問題如果resize()比原size小則刪去超過的,如果大則用value的默認構造函數構造對象填充空間。

list內部數據結構是雙向環狀鏈表。物理存儲空間可以是不連續的,通過指針來進行數據的訪問,這個特點使得它沒有提供[]操作符的重載,隨機存取變的非常沒有效率但由於這個特點,它可以以很好的效率支持任意地方的刪除和插入。需要大量的插入和刪除,而不關心隨機存取,則應使用list。

deque物理上是分段連續的存儲空間,由緩衝區buff和一箇中控器組成(指向buff的指針的數組),通過中控器將buff連接起來,給上層用戶造成一種連續空間的假象。不適合中間插入但適合頭尾,也可以隨機訪問數據,是list和vector的折中方案。需要隨機存取,而且關心兩端數據的插入和刪除,則應使用deque。

deque插入刪除中間元素時,會比較到首近還是尾近,來進行數據移動。而在首尾增加數據也可能使迭代器失效的原因是,可中控器map的首或尾節點(buff指針)指向的buff已經沒位置了,且map的首或尾已經沒有了節點備用空間,這時將換一個更大的map將節點轉移過去。

map是STL的一個關聯容器,它提供一對一的數據映射能力,底層是紅黑樹,鍵值對用存儲在紅黑樹結點中的pair對象實現。要存儲一個數據字典,並要求方便地根據key找value,那麼map是較好的選擇。要查找一個元素是否在某集合內,則使用set存儲這個集合比較好。

表中有誤,set和map都不能隨機訪問,map的以下標訪問也是用查找實現的,rbtree迭代器是bidirectional_iterator_tag雙向迭代器。rbtree最大\小元素迭代器++\--都指向head,head的left是最小right是最大,parent是root。head迭代器減減是最大元素,++則會向右一下再一直向左,因此到中間某個不能事先確定的元素。

Input Iterator和Output Iterator不同的是,Forward Iterator可以對訪問過的元素進行訪問(如果前面保存了迭代器的話),但輸入輸出迭代器就不保證解引用得到的是正確結果

map/hash_map迭代器指向的是底層RBTree/hash table存儲的key-value pair。

hash底層需要設置(通過模板參數,key爲內置類型除外)hash()和equal_key(),RBTree需要設置compare()(默認是less<key>,這個模板裏面還是用的’<’ ’>’,所以只要重載過符號就行),所以還是map而非hash_map方便一點。

9.makefile

一個文件依賴庫a,庫a依賴庫b,寫makefile的時候,a要放在b的前面還是後面

a放b前面如gcc -o bin A B鏈接時會把a所需要的符號找出來,然後繼續往後走,找這些符號有沒有出現,如果走到末尾還沒出現,就報錯(前後上下順序都需如此,所以後面的makefile例子是可執行文件在最上面)如果a和b互相依賴呢?嘗試:bin a b a

makefile關係到了整個工程的編譯規則,(一個工程中的源文件不計數,其按類型、功能、模塊分別放在若干個目錄中),需要makefile定義系列的規則指定哪些文件需要先編譯,哪些文件需要後編譯哪些文件需要依賴哪些文件編譯,甚至於進行更復雜的功能操作因爲makefile就像一個Shell腳本一樣,其中也可以編寫操作系統的命令

makefile帶來的好處就是——「自動化編譯」,極大的提高了軟件開發的效率一旦寫好,只需要一個make命令,整個工程完全自動編譯make是一個命令解釋工具,是一個makefile中指令的解釋器make命令執行時,需要一個 Makefile 文件,以告訴make命令需要怎麼樣的去編譯和鏈接程序。

首先,我們用一個示例來說明Makefile的書寫規則。我們的規則是:
1)如果這個工程沒有編譯過,那麼我們的所有C文件都要編譯並被鏈接。
2)如果這個工程的某幾個C文件被修改,那麼我們只編譯被修改的C文件,並鏈接目標程序
3)如果這個工程的頭文件被改變了,那麼我們需要編譯引用了這幾個頭文件的C文件,並鏈接目標程序。

只要我們的Makefile寫得夠好把這個內容保存在文件爲「Makefile」或「makefile」的文件中,然後在該目錄下直接輸入命令「make」,make命令會自動智能地根據當前的文件修改的情況來確定哪些文件需要重編譯,從而自己編譯所需要的文件和鏈接目標程序。

 

target:dependence下面一行是命令,表示如果有文件比target文件要新的話,command所定義的命令就會被執行

clean可以寫成clean:rm prog.o code.o(內容任加),是執行make clean將執行的命令。

gcc -c test.c將生成test.o的目標文件

gcc -o app test.c將生成可執行程序app

gcc -c a.c -o a.o表示把源文件a.c編譯成指定文件名

10. 非靜態的成員函數必須被綁定到一個類的對象或者指針上,才能得到被調用對象的this指針,然後才能調用指針所指的成員函數(所有類的對象都有自己成員變量,但是成員函數都是共用的,爲了區分是誰調用了成員函數,就必須有this指針,this指針是隱式的添加到函數參數列表裏去)。

11. A* arr=new A[10];此時A的默認構造函數就被調用了10次,因爲之後A[n]是對一個已經創建的具體對象的操作。

12.

 

每個進程都有各自獨立的4G邏輯地址,其中0~3G是用戶態空間,3~4G是內核空間

靜態區域

text segment(代碼段):包括只讀存儲區和文本區,其中只讀存儲區存儲字符串常量,文本區存儲程序的機器代碼

data segment(數據段):存儲程序中已初始化的全局變量和靜態變量

bss segment:存儲未初始化的全局變量和靜態變量(局部+全局),以及所有被初始化爲0的全局變量和靜態變量,對於未初始化的全局變量和靜態變量,程序運行main之前時會統一初始化爲0。

動態區域

heap(堆):需要自己動態開闢,和釋放,可運行時確定大小,調用malloc時分配一個堆,允許在程序運行時才決定需要多大內存(動態分配內存),從低地址向高地址增長。 操作系統有一個記錄空閒內存地址的鏈表,當系統收到程序的申請時會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序,系統會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,delete語句才能正確的釋放內存空間。另外,由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多餘的那部分重新放入空閒鏈表中。

stack(棧):由系統自動分配,程序員無法控制,使用棧空間存儲局部變量、函數的返回地址、參數、返回值,從高地址向低地址增長。在創建進程時會有一個最大棧大小。

13. C++函數中參數的傳遞方式有指針傳遞、值傳遞、引用傳遞(1.比指針更安全2.節省了賦值給形參的時間空間開銷)(C中無論是指針、數組都是值傳遞,實參的值賦給形參

14. struct和class區別在於默認的繼承訪問控制權限、默認的成員訪控權限、struct可以用{}初始化、class還可以作爲模板參數,比如template <class T, int i>。

 

15. C和C++有什麼不同?1.設計思想2.特性3.語法4.場景、效率

設計思想上:C是面向過程的結構化編程語言,側重於解決問題的邏輯設計,基本單元是模塊(即函數把功能細分,一個一個功能編寫),C++是面向對象的的語言,側重於類的設計,基本單元是類/對象(把複雜業務抽象爲類,讓類相互產生關係,進行編程,更靈活,複用與擴展性更強)。 C++兼容C,是它的擴充。

語法上:C++具有封裝、繼承和多態三種特性C++有new delete、引用、類、函數重載。C++相比C,增加多許多類型安全的功能,比如類型轉換C++支持範式編程,比如模板類、函數模板等

場景:C語言用在效率高的場景(一般是直接訪問硬件),如嵌入式開發、操作系統的內核;C++用在比較上層、複雜的場景,如桌面應用軟件、服務器後臺。

要用特定的語言完成它所擅長適合的場景的任務。

16. ::operator new是一個關鍵字(C++語言預留的標識符)或者說運算符,在堆上分配一塊內存,並會自動調用類的構造函數。它包括負責堆內存分配的可重載的類內operator new函數,和調用類構造函數,兩個部分。 重載類內operator new,並且增加額外的參數,但是必須保證第一個參數必須是size_t類型,它指明瞭分配內存塊的大小

類內operator new可同時重載placement版本和普通版,它們不同就是size_t參數之後的參數。

內存不足時在拋出bad_alloc異常前如果客戶set_new_handler()了一個異常處理函數,那麼就先調用它。

調用格式都是  new(size_t之後的參數)類型名

17. 全局對象的構造函數會在main函數之前執行,以此可以實現在main前調用函數。

18. 語法是inline與函數定義體放一起,調用處直接展開,免除調用(尋址入口、運行代碼區跳轉),回調的開銷,提高效率。內聯函數(C++)會做參數類型檢查就地展開(編譯期)帶參數的宏(C)只做文本替換(預編譯期),所以內聯安全。宏不是函數而內聯是函數。宏在定義時要小心處理宏參數,(一般情況是把參數用括弧括起來)

類內定義的函數都爲內聯,編譯器也會進行一些自動優化,將一些短小函數內聯。

18. 具體的:

  1. define不會做類型檢查,const擁有類型,會執行相應的類型檢查
  2. define僅僅是文本替換,不爲它分配內存,而const參與編譯運行,會爲它分配內存,常量在程序的常量表中。 

19. C++預定義宏__FILE__ __DATA__ __TIME__ __LINE__ __CPLUSPLUS。

19. 局部變量,定義時創建,編譯單元結束時銷燬。全局變量從定義到源程序結束。

靜態變量伴隨整個源程序。操所繫統通過內存分配的段(棧、數據段/bss)來辨別。

20. malloc與free是C/C++標準庫函數,new/delete是C++關鍵字,都可以申請/釋放動態內存,但前者是庫函數,沒有調用非內部數據類型的構造/析構的部分,C++是面向對象的語言,需要調用進行對象的創建和銷燬,所以C++需要後者。free可能會造成資源的泄漏,特別是在繼承鏈中。

new的參數是數據類型,malloc的是字節大小new返回的是指定對象的指針,而malloc返回的是void*,因此malloc的返回值一般都需要進行類型轉化

new內存分配失敗拋出bad_malloc,malloc內存分配失敗返回NULL值

malloc分配的內存不夠的時候可以使用realloc擴容,new沒有這樣的操作

21.extern用於聲明文件外部的一個全局變量。

21. ①訪問寄存器要比訪問內存要塊,因此CPU會優先訪問該數據在寄存器中的存儲結果,但是內存中的數據可能已經發生了改變,而寄存器中還保留着原來的結果。爲了避免這種情況的發生將該變量聲明爲volatile,告訴CPU變量是易被改變的,每次都應該從內存去讀取數據一個參數可以即是const又是volatile的嗎?可以,一個例子是隻讀狀態寄存器,是const告訴程序不應該試圖去修改是volatile因爲它可能被意想不到的被改變

21. sizeof(p)比sizeof(int)來判斷OS的位數更靠譜,因爲64位OS的int也是4,但p爲8,(但是還和編譯器模式有關)。如果不允許用sizeof則用unsigned int a=~0;

22. 很多時候直到運行時才知道需要多大內存(動態分配內存),所以需要堆。

23. Debug爲調試版,包含大量調試信息(比如斷點),不做任何優化,體積較大便於調試程序。

Release稱爲發佈版,進行了優化使程序代碼大小和運行速度都最優,便於用戶直接運行使用。

24. ASCII有些字符不能從鍵盤輸入,用\+八進制數又不方便記憶,所以有\+字母如n的轉義字符。

25. 引用與指針,本質上指針是地址,引用是別名1.引用定義時必須被初始化,指針不必,所以引用比指針使用起來更安全2.引用初始化後不能改變,指針可以3.不存在空引用但存在空指針4.指針作爲參數是值傳遞,引用是引用傳遞

場景:能用引用盡量用,相對更安全高效(指針有可能變野,引用可能指向被釋放的棧空間),引用作爲函數參數不用將實參對象拷貝爲形參傳入,節省開銷,作爲返回值,如重載輸出運算符函數的返回值(返回),也是節省開銷。如果使用一個變量並讓它指向一個對象,但是該變量在某些時候也可能不指向任何對象,或者改變指向的對象,比如鏈表用指針實現,這時應該把變量聲明爲指針,因爲這樣可以賦空值給該變量

26. 用常量方便修改多個地方的變量值,如果直接在表達式中寫數字,會使可讀性變差或出現書寫錯誤,且更改麻煩。

27. 構造函數爲什麼不能是虛的。構造函數是虛函數就需要通過指向虛函數表的vptr來調用,可是對象還沒有實例化,也就是內存空間還沒有,找不到虛表指針這是一個死鎖

28. 設計一個類只能在堆上創建,可以將構造、複製構造、賦值設爲私有,再創建類內靜態函數return new。設計一個類只能在棧上創建,可以將operator new和delete設爲private或=delete,或者類似只在堆上創建的類的設計。

29. virtual函數返回值可以是基類虛函數返回值的子類或子類指針,定義了缺省參數值的參數的值是靜態綁定,所以不要重新定義virtual函數的缺省參數值。

30. C++不具備類型安全(不同類數據不能相互轉化),即不是強類型語言(如int/char可以作爲char/int操作,指針可以強轉)

31. virtual--多態、抽象類。

32. const ①修飾局部/全局變量,常量,需要初始化 ②常量指針或常量的指針 ③const修飾函數形參使參數不能改變、const修飾引用作爲函數參數使得不能改變(可以處理const和非const實參)且如果實參不匹配(或者爲一個左值如一個數字或表達式)可以創建臨時變量轉換成參數規定類型(如double轉int,這是函數參數的普通隱式轉換所做不到的)

類中:④const修飾成員變量,需要在構造函數中初始化或類定義中默認初始化⑤const修飾成員函數爲常函數,隱式傳入const *this,不可修改非靜態成員變量 ⑥成員函數返回const表明返回的不是左值

33. 空類sizeof爲1,因爲要爲對象申請內存空間,而申請的最小單位爲1。

34. 函數模板模板參數只能作爲形參、返回參,實例化時的模板參數是由編譯器在處理函數調用時根據調用傳入的參數自動指定的(模板參數自動推導)(也可以顯式指明)。類模板可以將成員變量、成員函數作爲模板參數,實例化的模板參數必須由程序員在類聲明中顯示指定

35. 重載主要靠函數簽名(函數名、形參)區分、調用函數,而與返回值無關。

函數參數不匹配時都會隱式轉換成正確類型(如果可能的話,如int轉double,反之不行)。

36. 模板是C++實現泛型編程的機制(不考慮數據類型只考慮邏輯),編譯時檢查數據類型,保證安全性,有很高的複用性,體現了通用和泛化的思想。容器是STL中特定的模板類。

38. a++不能作爲左值,而++a可以。

39. return的對象不能是指向棧內存的指針或引用,否則出現野指針或引用一個不確定的量,返回對象要考慮效率,因爲會複製臨時變量。

40. 不能重載.,不能重載非運算符,重載不能改變運算符優先級。

41. void*可以轉爲任意,但任意不能轉爲void*,必須強制。void*因爲不知類型,所以不能++。 函數參數可以用void* 。

42. 內聯函數(調用處直接展開成一段代碼,不存在尋址,編譯期要確定,寫了virtual編譯器就不把它當內聯了)、靜態成員函數(屬於整個類而不是具體對象,不被繼承,在子類中也可調用但受權限控制,即和在類外訪問無區別)、友元函數(不是成員不涉及繼承)、構造函數、非成員函數都不可以是虛函數。

43. 空類默認產生:缺省構造、缺省複製、析構、賦值函數

44. return string(s1+s2);編譯器直接把這個臨時對象創建並初始化到外部存儲單元中,省去了拷貝和析構的成本。因爲移動語義

45. vector本質上是一個會自動增加長度的動態數組。

46. 異常--運行時產生的問題在C++中表示爲異常對象throw的異常必須事先定義,是一個值或對象。用try、catch捕獲拋出異常時,將停止當前函數的執行,釋放try塊內存,檢查與該try相關的catch子句,看是否可以處理該異常。如果不能處理,就退出當前函數,並且釋放當前函數的內存並銷燬局部對象,繼續到上層的調用函數中查找,直到找到一個可以處理該異常的catch。這個過程稱爲棧解退找到匹配再運行catch編譯單元中的代碼

47. C++和Java之間的區別? 

Java的應用在高層,C++在中間層和底層

運行效率上比不過C++

Java語言簡潔;取消了指針帶來更高的代碼質量;

完全面向對象,java運行在jvm上(jvm可以做到與OS或CPU無關),獨特的運行機制使其具有可移植性非常高。

Java在web應用上具有C++無可比擬的優勢

垃圾回收機制的區別。C++用析構函數回收垃圾,Java自動回收,寫C和C++程序時一定要注意內存的申請和釋放。

Java用接口(Interface)技術,C++程序中需要繼承抽象類

48. 從語法上來說,構造函數和析構函數都可以拋出異常。但從邏輯上和風險控制上,構造函數不建議,析構函數不可以拋出異常。

構造函數中拋出異常,會導致析構函數不能被調用可能導致堆內存泄漏或系統資源未釋放(此時成員變量的內存已釋放了),必須保證在構造函數拋出異常之前,把資源釋放掉(可以用shared_ptr),防止內存泄露。

如果析構函數拋出異常,則異常點之後的程序不會執行,如果析構函數在異常點之後執行了某些必要的動作比如釋放某些資源,則這些動作不會執行,會造成諸如資源泄漏的問題。②某一個異常發生時,c++的棧解退(尋找匹配異常類型的catch)會調用局部對象的析構函數來釋放資源此時若析構函數本身也拋出異常,則前一個異常尚未處理,又有新的異常,會造成程序過早結束或不明確行爲

如果某個操作可能會拋出異常,class應提供一個普通函數(而非析構函數),來執行該操作此操作可以客戶執行,給客戶一個處理錯誤的機會。如果析構函數中異常非拋不可,那就用try catch來將異常吞下,必須要把這種可能發生的異常完全封裝在析構函數內部,決不能讓它拋出函數之外。

49. 智能指針auto_ptr採用所有權模式,對象只能被一個指向,可能賦值產生懸掛指針,被擯棄。

unique也是所有權模式,但不能賦值,是auto的替代,比它更安全。

shared共享所有權模式,計數機制引用計數爲0時,資源被釋放,可以另外三種指針構造,.get()返回真實指針,解決了unique的侷限性。

weak指向一個 shared_ptr 管理的對象只是提供了對管理對象的一個訪問手段只可以從一個 shared_ptr 或另一個 weak_ptr 對象構造拋出異常時,將停止當前函數的執行,釋放try塊內存,檢查與該try相關的catch子句,看是否可以處理該異常。如果不能處理,就退出當前函數,並且釋放當前函數的內存並銷燬局部對象,繼續到上層的調用函數中查找,直到找到一個可以處理該異常的catch。這個過程稱爲棧解退找到匹配再運行catch編譯單元中的代碼

47. C++和Java之間的區別? 

Java的應用在高層,C++在中間層和底層

運行效率上比不過C++

Java語言簡潔;取消了指針帶來更高的代碼質量;

完全面向對象,java運行在jvm上(jvm可以做到與OS或CPU無關),獨特的運行機制使其具有可移植性非常高。

Java在web應用上具有C++無可比擬的優勢

垃圾回收機制的區別。C++用析構函數回收垃圾,Java自動回收,寫C和C++程序時一定要注意內存的申請和釋放。

Java用接口(Interface)技術,C++程序中需要繼承抽象類

48. 從語法上來說,構造函數和析構函數都可以拋出異常。但從邏輯上和風險控制上,構造函數不建議,析構函數不可以拋出異常。

構造函數中拋出異常,會導致析構函數不能被調用可能導致堆內存泄漏或系統資源未釋放(此時成員變量的內存已釋放了),必須保證在構造函數拋出異常之前,把資源釋放掉(可以用shared_ptr),防止內存泄露。

如果析構函數拋出異常,則異常點之後的程序不會執行,如果析構函數在異常點之後執行了某些必要的動作比如釋放某些資源,則這些動作不會執行,會造成諸如資源泄漏的問題。②某一個異常發生時,c++的棧解退(尋找匹配異常類型的catch)會調用局部對象的析構函數來釋放資源此時若析構函數本身也拋出異常,則前一個異常尚未處理,又有新的異常,會造成程序過早結束或不明確行爲

如果某個操作可能會拋出異常,class應提供一個普通函數(而非析構函數),來執行該操作此操作可以客戶執行,給客戶一個處理錯誤的機會。如果析構函數中異常非拋不可,那就用try catch來將異常吞下,必須要把這種可能發生的異常完全封裝在析構函數內部,決不能讓它拋出函數之外。

49. 智能指針auto_ptr採用所有權模式,對象只能被一個指向,可能賦值產生懸掛指針,被擯棄。

unique也是所有權模式,但不能賦值,是auto的替代,比它更安全。

shared共享所有權模式,計數機制引用計數爲0時,資源被釋放,可以另外三種指針構造,.get()返回真實指針,解決了unique的侷限性。

weak指向一個 shared_ptr 管理的對象只是提供了對管理對象的一個訪問手段只可以從一個 shared_ptr 或另一個 weak_ptr 對象構造, 它的構造和析構不會引起引用計數的增加或減少。目的是爲配合 shared_ptr工作, 解決shared_ptr環形引用(如相互引用)時的死鎖問題

50. 類析構順序派生類本身的析構函數;對象成員析構函數;基類析構函數。

51. 必須在構造函數初始化列表進行初始化的const成員、引用成員、沒有默認構造函數的類成員。

52. 四種類型轉換

const_cast用於將const變量轉爲非const

static_cast用的最多,用於各種編譯期隱式轉換,非const轉const,void*轉指針等, static_cast能用於多態指針的向上轉化,因爲是編譯期向下轉能成功但是不安全,沒有運行時類型檢查

dynamic_cast用於動態類型轉換有rtti運行時類型檢查只能用於包含虛函數的類層次結構,用於類層次間轉指針或引用向上轉化和安全的向下轉化(�style="margin-left:0pt;">運行效率上比不過C++

Java語言簡潔;取消了指針帶來更高的代碼質量;

完全面向對象,java運行在jvm上(jvm可以做到與OS或CPU無關),獨特的運行機制使其具有可移植性非常高。

Java在web應用上具有C++無可比擬的優勢

垃圾回收機制的區別。C++用析構函數回收垃圾,Java自動回收,寫C和C++程序時一定要注意內存的申請和釋放。

Java用接口(Interface)技術,C++程序中需要繼承抽象類

48. 從語法上來說,構造函數和析構函數都可以拋出異常。但從邏輯上和風險控制上,構造函數不建議,析構函數不可以拋出異常。

構造函數中拋出異常,會導致析構函數不能被調用可能導致堆內存泄漏或系統資源未釋放(此時成員變量的內存已釋放了),必須保證在構造函數拋出異常之前,把資源釋放掉(可以用shared_ptr),防止內存泄露。

如果析構函數拋出異常,則異常點之後的程序不會執行,如果析構函數在異常點之後執行了某些必要的動作比如釋放某些資源,則這些動作不會執行,會造成諸如資源泄漏的問題。②某一個異常發生時,c++的棧解退(尋找匹配異常類型的catch)會調用局部對象的析構函數來釋放資源此時若析構函數本身也拋出異常,則前一個異常尚未處理,又有新的異常,會造成程序過早結束或不明確行爲

如果某個操作可能會拋出異常,class應提供一個普通函數(而非析構函數),來執行該操作此操作可以客戶執行,給客戶一個處理錯誤的機會。如果析構函數中異常非拋不可,那就用try catch來將異常吞下,必須要把這種可能發生的異常完全封裝在析構函數內部,決不能讓它拋出函數之外。

49. 智能指針auto_ptr採用所有權模式,對象只能被一個指向,可能賦值產生懸掛指針,被擯棄。

unique也是所有權模式,但不能賦值,是auto的替代,比它更安全。

shared共享所有權模式,計數機制引用計數爲0時,資源被釋放,可以另外三種指針構造,.get()返回真實指針,解決了unique的侷限性。

weak指向一個 shared_ptr 管理的對象只是提供了對管理對象的一個訪問手段只可以從一個 shared_ptr 或另一個 weak_ptr 對象構造, 它的構造和析構不會引起引用計數的增加或減少。目的是爲配合 shared_ptr工作, 解決shared_ptr環形引用(如相互引用)時的死鎖問題

50. 類析構順序派生類本身的析構函數;對象成員析構函數;基類析構函數。

51. 必須在構造函數初始化列表進行初始化的const成員、引用成員、沒有默認構造函數的類成員。

52. 四種類型轉換

const_cast用於將const變量轉爲非const

static_cast用的最多,用於各種編譯期隱式轉換,非const轉const,void*轉指針等, static_cast能用於多態指針的向上轉化,因爲是編譯期向下轉能成功但是不安全,沒有運行時類型檢查

dynamic_cast用於動態類型轉換有rtti運行時類型檢查只能用於包含虛函數的類層次結構,用於類層次間轉指針或引用向上轉化和安全的向下轉化(指向一個比將要轉化爲的指針的類更下層的對象時)不安全則返回空指針,引用則返回bad_cast異常。

reinterp#000000;">如果析構函數中異常非拋不可,那就用try catch來將異常吞下,必須要把這種可能發生的異常完全封裝在析構函數內部,決不能讓它拋出函數之外。

49. 智能指針auto_ptr採用所有權模式,對象只能被一個指向,可能賦值產生懸掛指針,被擯棄。

unique也是所有權模式,但不能賦值,是auto的替代,比它更安全。

shared共享所有權模式,計數機制引用計數爲0時,資源被釋放,可以另外三種指針構造,.get()返回真實指針,解決了unique的侷限性。

weak指向一個 shared_ptr 管理的對象只是提供了對管理對象的一個訪問手段只可以從一個 shared_ptr 或另一個 weak_ptr 對象構造, 它的構造和析構不會引起引用計數的增加或減少。目的是爲配合 shared_ptr工作, 解決shared_ptr環形引用(如相互引用)時的死鎖問題

50. 類析構順序派生類本身的析構函數;對象成員析構函數;基類析構函數。

51. 必須在構造函數初始化列表進行初始化的const成員、引用成員、沒有默認構造函數的類成員。

52. 四種類型轉換

const_cast用於將const變量轉爲非const

static_cast用的最多,用於各種編譯期隱式轉換,非const轉const,void*轉指針等, static_cast能用於多態指針的向上轉化,因爲是編譯期向下轉能成功但是不安全,沒有運行時類型檢查

dynamic_cast用於動態類型轉換有rtti運行時類型檢查只能用於包含虛函數的類層次結構,用於類層次間轉指針或引用向上轉化和安全的向下轉化(指向一個比將要轉化爲的指針的類更下層的對象時)不安全則返回空指針,引用則返回bad_cast異常。

reinterpret_cast幾乎什麼都可以轉,比如將int轉指針,但這是非常危險的,可能會出問題,儘量少用;

爲什麼不使用C的強制轉換?C的強制轉換表面上看起來功能強大什麼都能轉,但是轉化太過鬆散不夠明確,不能進行類型安全性檢查

53. RTTI是運行時類型識別,只能用於包含虛函數的類層次結構,對於沒有虛函數的層次結構,編譯器一律按照靜態聯編rtti通過虛函數表指針對應到虛函數表根據表中的type_info對象的指針判斷類型,所以必須要虛函數),有3個支持的運算符dynamic_cast已經說了。typeid(對象或類)函數,返回一個重載了==的type_info對象,可判斷指針指向的倆對象是否同類。多態類的對象的類型信息保存在虛函數表的索引的-1的項中,該項是一個type_info對象的地址,該type_info對象保存着該對象對應的類型信息每個多態性質的類都對應着一個type_info對象

54. C++中的NULL就是0或者void*(0),會有問題,如fun(NULL);會匹配到參數爲int的重載函數,所以要nullptr。

55. 命名空間作爲附加信息來區分不同庫中相同名稱的函數、類、變量等

56. explicit關鍵字用在構造函數前面,使帶一個參數的構造函數的參數類型對應的變量不能隱式轉換爲該類對象。

57. 野指針指向非法的內存地址:指向的對象已消亡且指針未被置空,如被釋放的,或超出作用域已被釋放的,或未初始化時亂指一氣,或指向不可訪問的系統空間

58. 用C實現C++多態,父struct中有個函數指針,可以在子struct裏有一個父struct放在第一位,其中的父struct裏的函數指針指向定義爲需要重寫的子函數,將父struct指針指向子struct再調用函數就是子struct的函數了。

59. 腳本語言,計算機對代碼進行

相關文章
相關標籤/搜索