笔试题--Multicore简答题(下)
1.面向對象的特征
封裝
封裝,也就是把客觀事物封裝成抽象的類,并且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。
繼承
繼承是指這樣一種能力:它可以使用現有類的所有功能,并在無需重新編寫原來的類的情況下對這些功能進行擴展。
通過繼承創建的新類稱為“子類”或“派生類”;被繼承的類稱為“基類”、“父類”或“超類”;繼承的過程,就是從一般到特殊的過程,要實現繼承,可以通過“繼承”(Inheritance)和“組合”(Composition)來實現。
在某些 OOP 語言中,一個子類可以繼承多個基類。但是一般情況下,一個子類只能有一個基類,要實現多重繼承,可以通過多級繼承來實現。
繼承概念的實現方式有三類:實現繼承、接口繼承和可視繼承。
實現繼承是指使用基類的屬性和方法而無需額外編碼的能力;
接口繼承是指僅使用屬性和方法的名稱、但是子類必須提供實現的能力;
可視繼承是指子窗體(類)使用基窗體(類)的外觀和實現代碼的能力。
在考慮使用繼承時,有一點需要注意,那就是兩個類之間的關系應該是“屬于”關系。例如,Employee 是一個人,Manager 也是一個人,因此這兩個類都可以繼承 Person 類。但是 Leg 類卻不能繼承 Person 類,因為腿并不是一個人。
抽象類僅定義將由子類創建的一般屬性和方法,創建抽象類時,請使用關鍵字 Interface 而不是 Class。
OO開發范式大致為:劃分對象→抽象類→將類組織成為層次化結構(繼承和合成) →用類與實例進行設計和實現幾個階段。
多態
多態性(polymorphisn)是允許你將父對象設置成為和一個或更多的他的子對象相等的技術,賦值之后,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。
實現多態,有二種方式,覆蓋,重載。
覆蓋,是指子類重新定義父類的虛函數的做法。
重載,是指允許存在多個同名函數,而這些函數的參數表不同(或許參數個數不同,或許參數類型不同,或許兩者都不同)。
其實,重載的概念并不屬于“面向對象編程”,重載的實現是:編譯器根據函數不同的參數表,對同名函數的名稱做修飾,然后這些同名函數就成了不同的函數(至少對于編譯器來說是這樣的)。如,有兩個同名函數:function func(p:integer):integer;和function func(p:string):integer;。那么編譯器做過修飾后的函數名稱可能是這樣的:int_func、str_func。對于這兩個函數的調用,在編譯器間就已經確定了,是靜態的(記住:是靜態)。也就是說,它們的地址在編譯期就綁定了(早綁定),因此,重載和多態無關!真正和多態相關的是“覆蓋”。當子類重新定義了父類的虛函數后,父類指針根據賦給它的不同的子類指針,動態(記住:是動態!)的調用屬于子類的該函數,這樣的函數調用在編譯期間是無法確定的(調用的子類的虛函數的地址無法給出)。因此,這樣的函數地址是在運行期綁定的(晚邦定)。結論就是:重載只是一種語言特性,與多態無關,與面向對象也無關!引用一句Bruce Eckel的話:“不要犯傻,如果它不是晚邦定,它就不是多態。”
封裝可以隱藏實現細節,使得代碼模塊化;繼承可以擴展已存在的代碼模塊(類);它們的目的都是為了——代碼重用。而多態則是為了實現另一個目的——接口重用!多態的作用,就是為了類在繼承和派生的時候,保證使用“家譜”中任一類的實例的某一屬性時的正確調用。
2.描述下編譯階段
源代碼 (source code) → 預處理器 (preprocessor) → 編譯器 (compiler) → 匯編程序 (assembler) → 目標代碼 (object code) → 連接器 (Linker) → 可執行程序 (executables)?
(1)詞法分析
詞法分析器根據詞法規則識別出源程序中的各個記號(token),每個記號代表一類單詞(lexeme)。源程序中常見的記號可以歸為幾大類:關鍵字、標識符、字面量和特殊符號。詞法分析器的輸入是源程序,輸出是識別的記號流。詞法分析器的任務是把源文件的字符流轉換成記號流。本質上它查看連續的字符然后把它們識別為“單詞”。?
(2) 語法分析
語法分析器根據語法規則識別出記號流中的結構(短語、句子),并構造一棵能夠正確反映該結構的語法樹。?
(3) 語義分析
語義分析器根據語義規則對語法樹中的語法單元進行靜態語義檢查,如果類型檢查和轉換等,其目的在于保證語法正確的結構在語義上也是合法的。?
(4) 中間代碼生成(系統編譯的方式)
中間代碼生成器根據語義分析器的輸出生成中間代碼。中間代碼可以有若干種形式,它們的共同特征是與具體機器無關。最常用的一種中間代碼是三地址碼,它的一種實現方式是四元式。三地址碼的優點是便于閱讀、便于優化。?
(5)中間代碼優化
優化是編譯器的一個重要組成部分,由于編譯器將源程序翻譯成中間代碼的工作是機械的、按固定模式進行的,因此,生成的中間代碼往往在時間和空間上有很大浪費。當需要生成高效目標代碼時,就必須進行優化。?
(6)目標代碼生成
目標代碼生成是編譯器的最后一個階段。在生成目標代碼時要考慮以下幾個問題:計算機的系統結構、指令系統、寄存器的分配以及內存的組織等。編譯器生成的目標程序代碼可以有多種形式:匯編語言、可重定位二進制代碼、內存形式。?
(7 )符號表管理
符號表的作用是記錄源程序中符號的必要信息,并加以合理組織,從而在編譯器的各個階段能對它們進行快速、準確的查找和操作。符號表中的某些內容甚至要保留到程序的運行階段。?
(8) 出錯處理
用戶編寫的源程序中往往會有一些錯誤,可分為靜態錯誤和動態錯誤兩類。所謂動態錯誤,是指源程序中的邏輯錯誤,它們發生在程序運行的時候,也被稱作動態語義錯誤,如變量取值為零時作為除數,數組元素引用時下標出界等。靜態錯誤又可分為語法錯誤和靜態語義錯誤。語法錯誤是指有關語言結構上的錯誤,如單詞拼寫錯、表達式中缺少操作數、begin和end不匹配等。靜態語義錯誤是指分析源程序時可以發現的語言意義上的錯誤,如加法的兩個操作數中一個是整型變量名,而另一個是數組名等。
3.線程與進程的區別
(1)地址空間:進程內的一個執行單元;進程至少有一個線程;它們共享進程的地址空間;而進程有自己獨立的地址空間;
(2)資源擁有:進程是資源分配和擁有的單位,同一個進程內的線程共享進程的資源;
(3)線程是處理器調度的基本單位,但進程不是;
(4)二者均可并發執行。
4.堆和棧的區別
4.1申請方式?
stack:?
由系統自動分配。 例如,聲明在函數中一個局部變量 int b; 系統自動在棧中為b開辟空間?
heap:?
需要程序員自己申請,并指明大小,在c中malloc函數?
如p1 = (char *)malloc(10);?
在C++中用new運算符?
如p2 = new char[10];?
但是注意p1、p2本身是在棧中的。?
4.2?申請后系統的響應?
棧:只要棧的剩余空間大于所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。?
堆:首先應該知道操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大于所申請空間的堆結點,然后將該結點從空閑結點鏈表?
中刪除,并將該結點的空間分配給程序,另外,對于大多數系統,會在這塊內存空間中的?首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。?
另外,由于找到的堆結點的大小不一定正好等于申請的大小,系統會自動的將多余的那部分重新放入空閑鏈表中。?
4.3申請大小的限制?
棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意?思是棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M(也有?的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間較小。?
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由于系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小?
受限于計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
4.4申請效率的比較:?
棧由系統自動分配,速度較快。但程序員是無法控制的。?
堆是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便.?
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內存,他不是在堆,也不是在棧是?
直接在進程的地址空間中保留一塊內存,雖然用起來最不方便。但是速度快,也最靈活。
4.5堆和棧中的存儲內容?
棧: 在函數調用時,第一個進棧的是主函數中后的下一條指令(函數調用語句的下一條可?執行語句)的地址,然后是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然后是函數中的局部變量。注意靜態變量是不入棧的。?
當本次函數調用結束后,局部變量先出棧,然后是參數,最后棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。?
堆:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容由程序員安排。?
4.6存取效率的比較?
char s1[] = "aaaaaaaaaaaaaaa";?
char *s2 = "bbbbbbbbbbbbbbbbb";?
aaaaaaaaaaa是在運行時刻賦值的;?
而bbbbbbbbbbb是在編譯時就確定的;?
但是,在以后的存取中,在棧上的數組比指針所指向的字符串(例如堆)快。?
比如:?
#include?
void main()?
{?
char a = 1;?
char c[] = "1234567890";?
char *p ="1234567890";?
a = c[1];?
a = p[1];?
return;?
}?
對應的匯編代碼?
10: a = c[1];?
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]?
0040106A 88 4D FC mov byte ptr [ebp-4],cl?
11: a = p[1];?
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]?
00401070 8A 42 01 mov al,byte ptr [edx+1]?
00401073 88 45 FC mov byte ptr [ebp-4],al?
第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,再根據edx讀取字符,顯然慢了。
5.并發和并行
并發和并行的區別就是一個處理器同時處理多個任務和多個處理器或者是多核的處理器同時處理多個不同的任務。
前者是邏輯上的同時發生(simultaneous),而后者是物理上的同時發生。
并發性(concurrency),又稱共行性,是指能處理多個同時性活動的能力,并發事件之間不一定要同一時刻發生。
并行(parallelism)是指同時發生的兩個并發事件,具有并發的含義,而并發則不一定并行。
?
轉載于:https://www.cnblogs.com/siliconvalley/archive/2013/06/06/3121321.html
總結
以上是生活随笔為你收集整理的笔试题--Multicore简答题(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: lightbox自定义图片大小的实现
- 下一篇: MySQL索引的Index method