c/c++相關面試準備筆記1

在c++程序中調用被C編譯器編譯後的函數,爲何要加extern  「C」?  c++

  C++語言支持函數重載,C語言不支持函數重載。函數被C++編譯後在庫中的名字與C語言的不一樣。C++提供了C鏈接交換指定符號ectern  「C」解決名字匹配問題。程序員

頭文件中ifndef/define/endif是幹什麼用的?算法

  防止該頭文件被重複引用。編程

評價一下C與C++的各自特色。windows

  C是一種結構化語言,重點在於算法和數據結構。C程序的設計首先考慮的是如何經過一個過程,對輸入進行運算處理獲得輸出。而對於C++,首先考慮的是如何構造一個對象模型,讓這個模型可以契合與之對應的問題域,這樣就能夠經過獲取對象的狀態信息獲得輸出或實現過程控制。數組

const用途有哪些?安全

  在C程序中,const的用法主要有定義常量、修飾函數參數、修飾函數返回值。在C++程序中,它還能夠修飾函數的定義體,定義類中的某個成員函數爲恆態函數,即不改變類中的數據成員。被const修飾的東西都受到強制保護,能夠預防意外的變更,可以提升程序的健壯性。數據結構

const與#define相比有什麼不一樣?多線程

  C++語言能夠用const定義常量,也能夠用#define定義常量,可是前者比後者有更多的優勢:模塊化

  const常量有數據類型,而宏常量沒有數據類型。編譯器對前者進行類型安全檢查,而對後者只進行字符替換,沒有類型安全檢查,而且在字符替換中可能產生意料不到的錯誤;

  有些集成化的調試工具能夠對const常量進行調試,可是不能對宏常量進行調試。在C++程序中只使用const常量而不使用宏常量,即const常量徹底取代宏常量。

mutable關鍵字:

  在C++中,mutable是爲了突破const的限制而設置的。被mutable修飾的變量,將永遠處於可變的狀態,即便在一個const函數中,甚至結構體變量或者類對象爲const,其mutable成員也能夠被修改:

class  ST  { 
public:
    int a; 
    mutable int showCount; 
    void Show()const; 
}; 

void ST::Show()const{ 
    //a=1;//錯誤,不能在const成員函數中修改普通變量 
    showCount++;//正確 
}

  mutable只能修飾非靜態數據成員;

volatile修飾符:

  volatile修飾的數據編譯器不可對其進行執行期寄存於寄存器的優化。這種特性是爲了知足多線程同步、中斷、硬件編程等特殊需求。遇到這個關鍵字聲明的變量,編譯器對訪問該變量的代碼就再也不進行優化,從而能夠提供對特殊地址的直接訪問。主要用在以下幾個地方:

  中斷服務程序中修改的供其餘程序檢測的變量須要加volatile;

  多任務環境下各任務間共享的標誌加volatile;

  存儲器映射的硬件寄存器一般也要加volatile說明,由於每次對他的讀寫均可能有不一樣的意義;

數據對齊:指的是數據所在的內存地址必須是該數據長度的整數倍。字節對齊的細節和編譯器實現相關,但通常而言,知足三個準則:

  結構體變量的首地址可以被其最寬基本類型成員的大小所整除;

  結構體每一個成員相對於結構體首地址的偏移量都是成員大小的整數倍,若有須要編譯器會在成員之間加上填充字節;

  結構體的總大小爲結構體最寬基本類型成員大小的整數倍,若有須要編譯器會在最末一個成員以後加上填充字節。若是結構體內包含其餘結構,會將 其中的成員打散來看其最寬基本類型成員大小;

  可使用編譯器的pack指令調整結構體對齊方式。

sizeof與strlen的區別:

  sizeof操做符的結果類型是size_t,他在頭文件中的typedef爲unisigned int類型。該類型保證能容納實現所創建的最大對象的字節大小;

  sizeof是運算符,strlen是函數;

  sizeof能夠用類型作參數,strlen只能用char*做爲參數,且必須是以’\0’結尾的。

  數組作sizeof的參數不退化,傳遞給strlen就退化爲指針。

  大部分煸詡程序在編譯時就把sizeof計算過了,是類型或是變量的長度;

  strlen的結果要在運行的時候才能計算出來,用來計算字符串的長度,而不是類型佔內存的大小;

  sizeof後若是是類型必須加括號,若是是變量名能夠不加括號。這是由於sizeof是個操做符而不是函數;

  當使用了一個結構類型或變量時,sizeof返回實際的大小。當使用一靜態的空間數組時,sizeof返回所有數組的尺寸。sizeof操做符不能返回被動態分配的數組或外部的數組的尺寸。

  數組做爲參數傳給函數時傳的是指針而不是數組,傳遞的是數組的首地址,在C++中傳遞數組永遠都是傳遞指向數組首元素的指針,編譯器不知道數組的大小。若是想在函數內知道數組的大小,須要進入函數時加上一個數組長度的參數;

  計算結構變量的大小就必須討論數據對齊問題。

  sizeof操做符不能用於函數類型、不徹底類型或位字段。不徹底類型指具備未知存儲大小數據的數據類型,如未知存儲大小的數組類型、未知內容的結構或聯合類型、void類型。

sizeof的使用場合:

  sizeof操做符的一個主要用途就是與存儲分配和I/O系統那樣的例程進行通訊。

  用它來看某種類型的對象在內存中所佔的單元字節。

  在動態分配一對象時,可讓系統知道要分配多少內存;

  便於一些類型的擴充。在windows中有不少結構類型就有一個專門的字段用來存放該類型的字節大小;

  若是操做數是函數中的數組形參或函數類型的形參,建議在涉及操做數字節大小時用sizeof代替常量計算。

  若是操做數是函數中的數組形參或函數類型的形參,sizeof給出其指針的大小;

  空類的所佔內存爲1,單一繼承和多重繼承的空類的空間仍是1。可是涉及到虛繼承的空類所佔空間大小爲4字節,由於有虛表。

內聯函數和宏的差異?

  內聯函數和普通函數相比能夠加快程序運行的速度,由於不須要中斷調用,在編譯的時候內聯函數能夠直接被鑲嵌到目標代碼中。而宏只是一個簡單的替換。

  內聯函數要作參數類型檢查,這是內聯函數跟宏相比的優點;

  inline是指嵌入代碼,就是在跳用函數的地方不是跳轉,而是把代碼直接寫到哪裏去。對於短小的代碼來講,inline能夠帶來必定的效率提高,並且和C時代的宏函數相比,inline更安全可靠。但是這個是以增長空間消耗爲代價的。至因而否使用inline函數,就須要根據實際狀況來取捨了;

內聯一邊只用於下面的狀況:

  一個函數不斷被重複調用;

  函數只有簡單的幾行,且函數不包含for、whlie、switch語句;

  宏在C語言裏極其重要,而在C++裏面就用的少多了。關於宏的第一規則是毫不應該去使用它,除非你不得不這樣作。幾乎每一個宏都代表了在程序設計語言裏、程序裏或者程序員的一個缺陷,由於它將在編譯器看到程序的正文以前從新擺佈這些正文。宏也是許多程序設計工具的主要麻煩。因此,若是你使用了宏,就應該準備只能從各類工具中獲得較少的服務;

  用define定義常量能夠用const代替,可是主要const修飾的只讀變量不能用來定義數組的維度,也不能放在case關鍵字的後面;

  宏是在代碼處不加任何驗證的簡單替代,而內斂函數是將代碼直接插入到調用處,而減小了普通函數調用時的資源消耗。

  宏不是函數,只是在編譯前(編譯預處理階段)將程序中有關字符串替換成宏體。

  inline函數是函數,可是在編譯中不單獨產生代碼,而是將有關代碼嵌入到調用處;

指針和引用的區別:

  非空區別:在任何狀況下都不能使用指向空值的引用。一個引用必須老是指向某些對象。

  合法性區別:在使用引用以前不須要測試它的合法性。相反,指針則應該老是被測試,防止其爲空;

  可修改區別:指針能夠被從新賦值以指向另外一個不一樣的對象。可是引用則老是指向在初始化是被指向的對象,之後不能改變,可是指定的對象其內容能夠改變。

  應用區別:在如下狀況下應該使用指針:一是考慮到存在不指向任何對象的可能,二是須要可以在不一樣的時刻指向不一樣的對象。若是老是隻想一個對象而且一旦指向一個對象後就不會改變其指向,那麼應該使用引用。

寫出函數指針、函數返回指針:

  void (*f)()、void * f()

C++中有了malloc/free,爲何還須要new/delete?

  malloc與free是c++/c語言的標準函數,而new/malloc是c++的運算符。它們都用於申請動態內存和釋放內存;

  對於非內部數據類型的對象而言,只用malloc/free沒法知足動態對象的要求。對象在建立的同時要自動執行構造函數,對象在消亡以前要自動執行析構函數。因爲malloc/free是庫函數而不是運算符,不在編譯器控制權限以內,不可以把執行構造函數和析構函數的任務強加於malloc/free。

  所以c++須要一個能完成動態內存分配和初始化工做的運算符new,以及一個能完成清理與釋放工做的運算符delete。new/malloc不是庫函數,是運算符;

句柄與指針:

  句柄和指針實際上是兩個徹底不一樣的概念。windows系統用句柄表及系統資源,隱藏系統的信息。你只要知道有這個東西,而後去調用就好了,他是個32bit的uint。指針則標記某個物理內存地址,二者是不一樣的概念。

  句柄是一種指向指針的指針。

介紹一下STL和包容器,如何實現?

  c++的一個新特性就是採用了標準模板庫。標準模板庫是一個基於模版的容器類庫,包括鏈表、列表、隊列和堆棧。標準模板庫還包含許多經常使用的算法,包括排序和查找。

  標準模板庫的目的是提供對經常使用需求從新開發的一種替代方法。容器是包容其餘對象的對象。能夠幫助程序員處理一些經常使用的編程任務。標準模板庫容器類有兩種類型,分別爲順序和關聯。順序容器能夠提供對成員的順序訪問和隨機訪問。

什麼是泛型編程?

  stl表明用一致的方式編程是可能的。實際上,他徹底不一樣於咱們看到的c++編程,也徹底不一樣於大多數教科書裏所被描述的方式。stl並非試圖用c++編程,只是試圖找一種正確的方式來處理軟件,尋找一種能夠表達個人想法的語言而已。

  泛型編程是一種基於發現高效算法的最抽象表示的編程方法。也就是說,一算法爲起點並尋找能使其工做且有效率工做的最通常的必要條件集。泛型編程假定有某些基本的法則在支配軟件組件的行爲,而且基於這些法則有可能設計可互操做的模塊,甚至還可使用此法則去知道咱們的軟件設計。stl就是一個泛型編程的例子。c++一種但是使用泛型編程例子的語言。泛型編程中,算法不與任何特定的數據結構或對象類型系在一塊兒;

  初始化列表的初始化順序是根據成員變量的聲明順序來執行的。

 

面對對象編程帶來的便利有:良好的可複用性、易維護和良好的可擴充性;

面對對象的基本概念是什麼面對對象必須提供對象、類和繼承;

對於一個空類,編譯器默認產生四個成員函數:默認構造函數、析構函數、拷貝構造函數和賦值函數;

結構與類的區別:

  class中變量默認是private,struct中的變量默認是public。struct能夠有構造函數、析構函數,之間也能夠繼承,等。c++中的struct與class其實意義同樣,惟一的不一樣就是struct裏面默認的訪問控制是public,class中默認的訪問控制是private。c++中存在struct關鍵字的惟一意義就是爲了讓c程序員有個歸屬感,是爲了讓c++編譯器兼容之前的用c開發的項目;

多態:

  一種接口,多種方法;容許將子類類型的指針賦值給父類類型的指針。多態性是經過虛函數實現的。虛函數就是容許被其子類從新定義的成員函數。而子類從新定義父類虛函數的作法稱爲覆蓋或重寫。

覆蓋與重載:

  覆蓋是指子類從新定義父類的虛函數的作法。而重載,是指容許存在多個同名函數,而這些函數的參數表不一樣。重載的函數編譯器會給不一樣參數的函數修飾一個不一樣的名字是靜態的。

  封裝能夠隱藏實現細節,使得代碼模塊化;繼承能夠擴展已存在的代碼模塊;他們的目的都是爲了代碼重用;而接口是爲了實現接口重用。

友元:

  友元是一種定義在類外部的普通函數,但他須要在類體內進行說明,爲了與該類的成員函數加以區別,在說明時前面加以關鍵字friend。友元不是成員函數,可是他能夠訪問類中的私有成員。友元的做用在於提升程序的運行效率,可是它破壞了類的封裝性和隱藏性,使得非成員函數能夠訪問類的私有成員。

  類對象操做的時候在內部構造是會有一個隱形的this指針。因爲Car類是vehicle的派生類,那麼當car對象建立的時候,這個this指針就會覆蓋到vehicle類的範圍,因此派生類可以對基類成員進行操做。

公有繼承:

  基類成員對其對象的可見性與通常類及其對象的可見性相同,共有成員可見,其餘成員不可見。這裏保護成員與私有成員相同;在公有繼承中,派生類的對象能夠訪問基類中的公有成員,派生類的成員函數能夠訪問基類的公有成員和保護成員;

私有繼承:

  積累對其對象的可見性與通常類及其對象的可見性相同,公有成員可見,其餘成員不可見;基類的公有成員可和保護成員是可見的,基類的公有成員和保護成員都做爲派生類的私有成員,而且不能被這個派生類的子類所訪問;基類的私有成員都是不可見的,派生類不可訪問基類中的私有成員。因此在私有繼承時,積累的成員只能由派生類訪問,而沒法再往下繼承。

保護繼承:

  這種繼承類型與私有繼承的狀況相同。二者的區別僅在於對派生類的成員而言,基類成員對其對象的可見性與通常類及其對象的可見性相同,公有成員可見,其餘成員不可見。基類的公有成員和保護成員都做爲派生類的保護成員;基類的私有成員是不可見的,派生類不可訪問基類中的私有成員。

  基類中的私有成員只能被基類中的成員函數和友元函數訪問,不能被其餘函數訪問;在無繼承的類中,protected與private控制符是沒有差異的。在繼承中,基類的private對全部的外界都屏蔽(包括本身的派生類),基類的protected控制符對應用程序是屏蔽的,但對其派生類是可訪問的。

 

  每一個對象內都有一個虛表指針,指向虛函數表,虛表內存放了虛函數的地址。虛表是順序存放虛函數地址的,不須要用到鏈表。

  vptr和vtble和類對象的關係:每個具備虛函數的類都有一個虛函數表vtble,裏面按照在類中聲明的虛函數的順序存放着虛函數的地址,這個vtble是這個類全部對象所共有的。在每一個具備虛函數的類的對象裏面都有一個vptr虛函數指針,只想這個vtalbe首地址。

  虛繼承是多重繼承中特有的概念。虛擬基類是爲了解決多重繼承而出現的。虛繼承與虛函數繼承是徹底不一樣的概念。

  純虛函數是在基類中聲明的虛函數,它在基類中沒有定義,但要求任何派生類都要定義本身的實現方法。在基類中實現純虛函數的方法是在函數原型後加「=0」:virtual void funtion1()=0;

引入純虛函數的緣由:

  一、爲了方便使用多態特性,咱們經常須要在基類中定義虛擬函數。二、在不少狀況下,基類自己生成對象是不合情理的。例如生成一我的類對象。爲了解決上述問題,引入了虛函數的概念,將函數定義爲純虛函數,則編譯器要求在派生類中必須予以重寫以實現多態性。同時含有純虛函數的類稱爲抽象類,它不能生成對象。聲明純虛函數的類是一個抽象類,因此用戶不能建立類的實例,只能建立它的派生類的實例。

  定義一個虛函數,不表明虛函數爲不被實現的函數;定義它爲虛函數是爲了容許用基類的指針來調用子類的這個函數。定義一個函數爲純虛函數,才表明函數沒有被實現。規範繼承這個類的程序員必須實現這個函數。定義純虛函數的目的在於,使得派生類僅僅只是繼承函數的接口。

  在有動態分配堆上內存的時候,析構函數必須是虛函數,但沒有必要是純虛的;虛函數是c++中用於實現多態的機制。核心理念就是經過基類訪問派生類定義的函數。

  友元函數不是成員函數,只有成員函數才能夠是虛擬的,所以友元不能是虛擬函數。可是能夠經過讓友元函數調用虛擬成員函數來解決友元的虛擬問題。

  析構函數應當是虛函數,將調用相應對象類型的析構函數,所以若是指針指向的是子類對象,將調用子類的析構函數,而後自動調用基類的析構函數。

RTTI字面上理解就是執行時期的類型信息,其重要做用是動態判別執行時期的類型。由於虛函數有自己的侷限性,當設計類別階層時,須要判斷某個對象所屬的類別,而由於類別設計中大量使用了虛函數,因此使得這一工做難以實現,但又極其重要,因而使用RTTI的typrid運算符能使程序員肯定對象的動態類型。

 

中綴表達式轉後綴表達式:

  規則:從左到右邊遍歷中綴表達式的每一個數字和符號,如果數字就輸出,即成爲後綴表達式的一部分;如果符號,則判斷其與棧頂符號的優先級,是右括號或優先級低於棧頂符號則棧頂元素依次出棧並輸出,並將當前符號進棧,一直到最終輸出後綴表達式爲止。

相關文章
相關標籤/搜索