C++面試寶典
一、1byte = 8bit;1k = 1024byte;1M = 1024K;1G = 1024M;
二、
三、sizeof(struct)唯一原則就是按照計算機記憶體訪問最快的方式對齊。各成員變數存放的 起始地址相對於結構的起始地址的偏移量必須為該變數的型別所佔用的位元組數的倍數;總的位元組數一定是最大位元組數的整數倍。
(1)陣列名指代一種資料結構,這種資料結構就是陣列;
例如:
1 2 |
|
輸出結果為10,str指代資料結構char[10]。
(2)陣列名可以轉換為指向其指代實體的指標,而且是一個指標常量,不能作自增、自減等操作,不能被修改;
str++;//編譯出錯,提示str不是左值
(3)陣列名作為函式形參時,淪為普通指標。
WindowsNT32位平臺下,指標的長度(佔用記憶體的大小)為4位元組,故sizeof(str)、sizeof(p)都為4。
(4)strlen():strlen計算字串的長度,以'\0'為字串結束標誌,不包括\0
四、物件:面向物件是一種基於物件的、基於類的的軟體開發思想。面向物件具有繼承、封裝、多型的特性。
五、static關鍵字:
1、隱藏:當同時編譯多個檔案時,所有未加static字首的全域性變數和函式都具有全域性可見性。當然,加了static就具有了隱藏功能,只在該檔案中可見。利用這一特性可以在不同的檔案中定義同名函式和同名變數,而不必擔心命名衝突。static可以用作函式和變數的字首,對於函式來講,static的作用僅限於隱藏
2、static的第二個作用是保持變數內容的持久:共有兩種變數儲存在靜態儲存區:全域性變數和static變數,只不過和全域性變數比起來,static可以控制變數的可見範圍,說到底static還是用來隱藏的。雖然這種用法不常見。如果作為static區域性變數在函式內定義,它的生存期為整個源程式,但是其作用域仍與自動變數相同,只能在定義該變數的函式內使用該變數。退出該函式後, 儘管該變數還繼續存在,但不能使用它。靜態區域性變數:通常,在函式體內定義一個變數,每當程式執行到該語句時都會給該區域性變數分配棧記憶體。但隨著程式退出函式體,系統就會回收棧記憶體,區域性變數也相應失效。但有時候我們需要在兩次呼叫之間對變數的值進行儲存。通常的想法是定義一個全域性變數來實現。但這樣一來,變數已經不再屬於函式本身了不再受函式的控制,給函式的維護帶來不便。靜態區域性變數正好可以解決這個問題。靜態區域性變數儲存在全域性資料區,而不是儲存在棧中,每次的值儲存到下一次呼叫,直到下次賦新值。
3、static的第三個作用是預設初始化為0(static變數)
4、static的第四個作用:C++中的類成員宣告static:static用在類中,被static修飾的變數屬於類變數,對相於 public,protected,private 關鍵字的影響它和普通資料成員一樣。sizeof 運算子不會計算靜態成員變數,靜態成員必須在類的外面初始化,靜態資料成員初始化的格式如下:
<資料型別><類名>::<靜態資料成員名>=<值> 如:int A::s = 0;在類宣告之後
用靜態資料成員時,採用如下格式 <類名>::<靜態成員名>
在類中的static成員函式屬於整個類所擁有,這個函式不接收this指標,因而只能訪問類的static成員變數。
靜態變數啥時候初始化:C語言中其在程式碼執行之前初始化,屬於編譯期初始化。而C++中由於引入物件,物件生成必須呼叫建構函式,因此C++規定全域性或區域性靜態物件當且僅當物件首次用到時進行構造
六、C++:建構函式和解構函式能否為虛擬函式?
簡單回答是:建構函式不能為虛擬函式,而解構函式可以且常常是虛擬函式。
七、struct和union:結構體是將不同型別的資料組合成一個整體,是自定義型別。共同體(聯合體)是不同型別的幾個變數共同佔用一段記憶體。
八、儲存區域:在C++中,記憶體區分為5個區,分別是堆、棧、自由儲存區、全域性/靜態變數儲存區、常量儲存區;在C中,C記憶體區分為堆、棧、全域性/靜態變數儲存區、常量儲存區;
九、malloc跟new:new的本質也是去呼叫malloc函式!!同理,delete的本質是去呼叫free()函式。1,申請記憶體所在位置:new操作符從自由儲存區(free store)上為物件動態分配記憶體空間,而malloc函式從堆上動態分配記憶體。自由儲存區是C++基於new操作符的一個抽象概念,凡是通過new操作符進行記憶體申請,該記憶體即為自由儲存區。而堆是作業系統中的術語,是作業系統所維護的一塊特殊記憶體,用於程式的記憶體動態分配,C語言使用malloc從堆上分配記憶體,使用free釋放已分配的對應記憶體。那麼自由儲存區是否能夠是堆(問題等價於new是否能在堆上動態分配記憶體),這取決於operator new 的實現細節。自由儲存區不僅可以是堆,還可以是靜態儲存區(全域性資料區),這都看operator new在哪裡為物件分配記憶體。2、返回型別:new操作符記憶體分配成功時,返回的是物件型別的指標,型別嚴格與物件匹配,無須進行型別轉換,故new是符合型別安全性的操作符。而malloc記憶體分配成功則是返回void * ,需要通過強制型別轉換將void*指標轉換成我們需要的型別。3、記憶體分配失敗時的返回值:new記憶體分配失敗時,會丟擲bac_alloc異常,它不會返回NULL;malloc分配記憶體失敗時返回NULL。
在使用C語言時,我們習慣在malloc分配記憶體後判斷分配是否成功:
- int *a = (int *)malloc ( sizeof (int ));
- if(NULL == a)
- {
- ...
- }
- else
- {
- ...
- }
使用new時判斷是否申請記憶體成功:
- try
- {
- int *a = new int();
- }
- catch (bad_alloc)
- {
- ...
- }
- 如果你想順便了解下異常基礎,可以看http://www.cnblogs.com/QG-whz/p/5136883.htmlC++ 異常機制分析。
4、是否需要指定記憶體大小:使用new操作符申請記憶體分配時無須指定記憶體塊的大小,編譯器會根據型別資訊自行計算,而malloc則需要顯式地指出所需記憶體的尺寸。
- class A{...}
- A * ptr = new A;
- A * ptr = (A *)malloc(sizeof(A)); //需要顯式指定所需記憶體大小sizeof(A);
5、是否呼叫建構函式/解構函式:new/delete會呼叫物件的建構函式/解構函式以完成物件的構造/析構。而malloc則不會。6、對陣列的處理:C++提供了new[]與delete[]來專門處理陣列型別:A * ptr = new A[10];//分配10個A物件,使用new[]分配的記憶體必須使用delete[]進行釋放:delete [] ptr;至於malloc,它並知道你在這塊記憶體上要放的陣列還是啥別的東西,反正它就給你一塊原始的記憶體。7、記憶體不夠重新分配記憶體:使用malloc分配的記憶體後,如果在使用過程中發現記憶體不足,可以使用realloc函式進行記憶體重新分配實現記憶體的擴充。realloc先判斷當前的指標所指記憶體是否有足夠的連續空間,如果有,原地擴大可分配的記憶體地址,並且返回原來的地址指標;如果空間不夠,先按照新指定的大小分配空間,將原有資料從頭到尾拷貝到新分配的記憶體區域,而後釋放原來的記憶體區域。new沒有這樣直觀的配套設施來擴充記憶體。8、是否可以被過載:new/delete可以,malloc/free不
十、巨集:
巨集定義是C語言提供的一種預處理,又稱為巨集代換、巨集替換,簡稱“巨集”,用#define定義,如下:
#define Pi 3.1415926
寫一個“標準”巨集MIN,這個巨集輸入兩個引數並返回較小的一個:#defineMIN(A,B)((A)<=(B)?(A):(B))
注意:謹慎地將巨集定義中的“引數”和整個巨集用用括弧括起來。末尾不加分號。
十一、虛擬函式:C++中的虛擬函式的作用主要是實現了多型的機制。那些被virtual關鍵字修飾的成員函式,就是虛擬函式。虛擬函式的作用,用專業術語來解釋就是實現多型性。我們只需在把基類的成員函式設為virtual,其派生類的相應的函式也會自動變為虛擬函式。指向基類的指標在操作它的多型類物件時,會根據不同的類物件,呼叫其相應的函式,這個函式就是虛擬函式。
十二、手寫string類:
- class String
- {
- public:
- String(const char *str = NULL); // 普通建構函式
- String(const String &other); // 拷貝建構函式
- ~ String(void); // 解構函式
- String & operator =(const String &other); // 賦值函式
- private:
- char *m_data; // 用於儲存字串
- };
- //普通建構函式
- String::String(const char *str)
- {
- if(str==NULL)
- {
- m_data = new char[1]; // 得分點:對空字串自動申請存放結束標誌'\0'的空
- //加分點:對m_data加NULL 判斷
- *m_data = '\0';
- }
- else
- {
- int length = strlen(str);
- m_data = new char[length+1];
- strcpy(m_data, str);
- }
- }
- // String的解構函式
- String::~String(void)
- {
- delete [] m_data; // 或delete m_data;
- }
- //拷貝建構函式
- String::String(const String &other) // 得分點:輸入引數為const型
- {
- int length = strlen(other.m_data);
- m_data = new char[length+1];
- strcpy(m_data, other.m_data);
- }
- //賦值函式
- String & String::operator =(const String &other) // 得分點:輸入引數為const型
- {
- if(this == &other) //得分點:檢查自賦值
- return *this;
- delete [] m_data; //得分點:釋放原有的記憶體資源
- int length = strlen( other.m_data );
- m_data = new char[length+1];
- strcpy( m_data, other.m_data );
- return *this; //得分點:返回本物件的引用
- }
十二、const關鍵字作用:連結:https://www.nowcoder.com/questionTerminal/5c2f7a22343e40179bea3a3cdeace8fb
來源:牛客網
const關鍵字至少有下列n個作用:
(1)欲阻止一個變數被改變,可以使用const關鍵字。在定義該const變數時,通常需要對它進行初始化,因為以後就沒有機會再去改變它了;
(2)對指標來說,可以指定指標本身為const,也可以指定指標所指的資料為const,或二者同時指定為const;
(3)在一個函式宣告中,const可以修飾形參,表明它是一個輸入引數,在函式內部不能改變其值;
(4)對於類的成員函式,若指定其為const型別,則表明其是一個常函式,不能修改類的 成員變數;
十三、虛擬函式:
為什麼解構函式必須是虛擬函式?為什麼C++預設的解構函式不是虛擬函式:
將可能會被繼承的父類的解構函式設定為虛擬函式,可以保證當我們new一個子類,然後使用基類指標指向該子類物件,釋放基類指標時可以釋放掉子類的空間,防止記憶體洩漏。C++預設的解構函式不是虛擬函式是因為虛擬函式需要額外的虛擬函式表和虛表指標,佔用額外的記憶體。而對於不會被繼承的類來說,其解構函式如果是虛擬函式,就會浪費記憶體。因此C++預設的解構函式不是虛擬函式,而是隻有當需要當作父類時,設定為虛擬函式。
十四、#include
1,< >和" "區別:區別是若 #include "" 查詢成功,則遮蔽 #include <> 所能找到的同名檔案;否則再按照 #include <> 的方式查詢檔案。另外標準庫標頭檔案都放在 #include <> 所查詢的位置。一般來說 #include <> 的查詢位置是標準庫標頭檔案所在目錄, #include "" 的查詢位置是當前原始檔所在目錄。<>先去系統目錄中找標頭檔案,如果沒有在到當前目錄下找。所以像標準的標頭檔案 stdio.h、stdlib.h等用這個方法。而""首先在當前目錄下尋找,如果找不到,再到系統目錄中尋找。 這個用於include自定義的標頭檔案,讓系統優先使用當前目錄中定義的。
2,帶不帶.h:帶 .h 的標頭檔案是舊標準的,如果想用新的標準的標頭檔案就不要帶 .h