日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

面经——C/C++常见面试知识点总结附面试真题

發(fā)布時間:2023/12/10 c/c++ 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 面经——C/C++常见面试知识点总结附面试真题 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

參考:C/C++ 面試題
作者:zhaouc
發(fā)布時間: 2015-02-15 15:51:00
網(wǎng)址:https://blog.csdn.net/zhaouc/article/details/43835667

參考:C/C++常見面試知識點總結附面試真題----20210529更新
作者:kuweicai
發(fā)布時間: 2018-09-19 22:47:57
網(wǎng)址:https://blog.csdn.net/kuweicai/article/details/82779648?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-2.nonecase

目錄

  • 第一部分:計算機基礎
    • 1. C/C++內(nèi)存有哪幾種類型?
    • 2. 堆和棧的區(qū)別?
    • 4. 程序編譯的過程?
    • 5. 計算機內(nèi)部如何存儲負數(shù)和浮點數(shù)?
    • 6. 函數(shù)調用的過程?
    • 7. 左值和右值
    • 8. 什么是內(nèi)存泄漏?面對內(nèi)存泄漏和指針越界,你有哪些方法?你通常采用哪些方法來避免和減少這類錯誤?
  • 第二部分:C v.s. C++
    • 1. C和C++的區(qū)別?
    • 2. int fun() 和 int fun(void)的區(qū)別?
    • 3. const 有什么用途
    • 4. 在C中用const 能定義真正意義上的常量嗎?C++中的const呢?
    • 5. 宏和內(nèi)聯(lián)(inline)函數(shù)的比較?
    • 6. C++中有了malloc / free , 為什么還需要 new / delete?
    • 7. C和C++中的強制類型轉換?
    • 8. static 有什么用途
    • 9. 類的靜態(tài)成員變量和靜態(tài)成員函數(shù)各有哪些特性?
    • 10. 在C++程序中調用被C編譯器編譯后的函數(shù),為什么要加extern“C”?
    • 11. 頭文件中的 ifndef/define/endif 是干什么用的? 該用法和 program once 的區(qū)別?
    • 12. 當i是一個整數(shù)的時候++i和i++那個更快一點?i++和++i的區(qū)別是什么?
    • 1. C中static有什么作用
    • 2.C++中const有什么用?
    • 3. C與C++各自是如何定義常量的?有什么不同?
    • 4. 既然C++中有更好的const為什么還要使用宏?
    • 5. C++中引用和指針的區(qū)別?
    • 6. 說一說C與C++的內(nèi)存分配方式?
    • 7. new/delete 與 malloc()/free() 的區(qū)別?
    • 8. #include<a.h>和#include"a.h" 有什么區(qū)別?
    • 9. 在C++ 程序中調用被 C編譯器編譯后的函數(shù),為什么要加 extern "C"?
    • 10. C++中的什么是多態(tài)性? 是如何實現(xiàn)的?
    • 11. 什么是動態(tài)特性?
    • 12.什么是封裝?C++中是如何實現(xiàn)的?
    • 13. 什么是RTTI?
    • 14. 什么是拷貝構造函數(shù)?
    • 15. 什么是深淺拷貝?
    • 16.面向對象程序設計的優(yōu)點?
  • 第三部分:數(shù)組、指針 & 引用
    • 1. 指針和引用的區(qū)別?
    • 2. 引用占用內(nèi)存空間嗎?
    • 3. 三目運算符
    • 4. 指針數(shù)組和數(shù)組指針的區(qū)別
    • 5. 左值引用與右值引用
    • 6. 右值引用的意義
  • 第四部分:C++特性
    • 1. 什么是面向對象(OOP)?面向對象的意義?
    • 2. 解釋下封裝、繼承和多態(tài)?
    • 3. 什么時候生成默認構造函數(shù)(無參構造函數(shù))?什么時候生成默認拷貝構造函數(shù)?什么是深拷貝?什么是淺拷貝?默認拷貝構造函數(shù)是哪種拷貝?什么時候用深拷貝?
    • 4. 構造函數(shù)和析構函數(shù)的執(zhí)行順序?
    • 5. 虛析構函數(shù)的作用?
    • 6. 細看拷貝構造函數(shù)
    • 7. C++的編譯環(huán)境
    • 8. Most vexing parse

以下內(nèi)容部分整理自網(wǎng)絡,部分為自己面試的真題。

第一部分:計算機基礎

1. C/C++內(nèi)存有哪幾種類型?

C中,內(nèi)存分為5個區(qū):堆(malloc)、棧(如局部變量、函數(shù)參數(shù))、程序代碼區(qū)(存放二進制代碼)、全局/靜態(tài)存儲區(qū)(全局變量、static變量)和常量存儲區(qū)(常量)。此外,C++中有自由存儲區(qū)(new)一說。
全局變量、static變量會初始化為缺省值,而堆和棧上的變量是隨機的,不確定的。

2. 堆和棧的區(qū)別?

  • 1).堆存放動態(tài)分配的對象——即那些在程序運行時動態(tài)分配的對象,比如 new 出來的對象,其生存期由程序控制;
  • 2).棧用來保存定義在函數(shù)內(nèi)的非static對象,如局部變量,僅在其定義的程序塊運行時才存在;
  • 3).靜態(tài)內(nèi)存用來保存static對象,類static數(shù)據(jù)成員以及定義在任何函數(shù)外部的變量,static對象在使用之前分配,程序結束時銷毀;
  • 4).棧和靜態(tài)內(nèi)存的對象由編譯器自動創(chuàng)建和銷毀。

總的來說,堆是C語言和操作系統(tǒng)的術語,是操作系統(tǒng)維護的一塊動態(tài)分配內(nèi)存;自由存儲是C++中通過new與delete動態(tài)分配和釋放對象的抽象概念。他們并不是完全一樣。
從技術上來說,堆(heap)是C語言和操作系統(tǒng)的術語。堆是操作系統(tǒng)所維護的一塊特殊內(nèi)存,它提供了動態(tài)分配的功能,當運行程序調用malloc()時就會從中分配,稍后調用free可把內(nèi)存交還。而自由存儲是C++中通過new和delete動態(tài)分配和釋放對象的抽象概念,通過new來申請的內(nèi)存區(qū)域可稱為自由存儲區(qū)。基本上,所有的C++編譯器默認使用堆來實現(xiàn)自由存儲,也即是缺省的全局運算符new和delete也許會按照malloc和free的方式來被實現(xiàn),這時藉由new運算符分配的對象,說它在堆上也對,說它在自由存儲區(qū)上也正確。

4. 程序編譯的過程?

程序編譯的過程中就是將用戶的文本形式的源代碼(c/c++)轉化成計算機可以直接執(zhí)行的機器代碼的過程。主要經(jīng)過四個過程:預處理、編譯、匯編和鏈接。具體示例如下。
一個hello.c的c語言程序如下。

#include <stdio.h> int main() {printf("happy new year!\n");return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

其編譯過程如下:

5. 計算機內(nèi)部如何存儲負數(shù)和浮點數(shù)?

負數(shù)比較容易,就是通過一個標志位和補碼來表示。
對于浮點類型的數(shù)據(jù)采用單精度類型(float)和雙精度類型(double)來存儲,float數(shù)據(jù)占用32bit,double數(shù)據(jù)占用64bit,我們在聲明一個變量float f= 2.25f的時候,是如何分配內(nèi)存的呢?如果胡亂分配,那世界豈不是亂套了么,其實不論是float還是double在存儲方式上都是遵從IEEE的規(guī)范的,float遵從的是IEEE R32.24 ,而double 遵從的是R64.53。更多可以參考浮點數(shù)表示。
無論是單精度還是雙精度在存儲中都分為三個部分:

  • 1). 符號位(Sign) : 0代表正,1代表為負
  • 2). 指數(shù)位(Exponent):用于存儲科學計數(shù)法中的指數(shù)數(shù)據(jù),并且采用移位存儲
  • 3). 尾數(shù)部分(Mantissa):尾數(shù)部分
    其中float的存儲方式如下圖所示:

    而雙精度的存儲方式如下圖:

6. 函數(shù)調用的過程?

int main(void) {...d = fun(a, b, c);cout<<d<<endl;...return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

調用fun()的過程大致如下:

  • main()========
  • 1).參數(shù)拷貝(壓棧),注意順序是從右到左,即c-b-a;
  • 2).保存d = fun(a, b, c)的下一條指令,即cout<<d<<endl(實際上是這條語句對應的匯編指令的起始位置);
  • 3).跳轉到fun()函數(shù),注意,到目前為止,這些都是在main()中進行的;
  • fun()=====
  • 4).移動ebp、esp形成新的棧幀結構;
  • 5).壓棧(push)形成臨時變量并執(zhí)行相關操作;
  • 6).return一個值;
  • 7).出棧(pop);
  • 8).恢復main函數(shù)的棧幀結構;
  • 9).返回main函數(shù);
  • main()========
  • 。。。

7. 左值和右值

不是很嚴謹?shù)膩碚f,左值指的是既能夠出現(xiàn)在等號左邊也能出現(xiàn)在等號右邊的變量(或表達式),右值指的則是只能出現(xiàn)在等號右邊的變量(或表達式)。舉例來說我們定義的變量 a 就是一個左值,而malloc返回的就是一個右值。或者左值就是在程序中能夠尋值的東西,右值就是一個具體的真實的值或者對象,沒法取到它的地址的東西(不完全準確),因此沒法對右值進行賦值,但是右值并非是不可修改的,比如自己定義的class, 可以通過它的成員函數(shù)來修改右值。

歸納一下就是:

  • 可以取地址的,有名字的,非臨時的就是左值
  • 不能取地址的,沒有名字的,臨時的,通常生命周期就在某個表達式之內(nèi)的就是右值

8. 什么是內(nèi)存泄漏?面對內(nèi)存泄漏和指針越界,你有哪些方法?你通常采用哪些方法來避免和減少這類錯誤?

用動態(tài)存儲分配函數(shù)動態(tài)開辟的空間,在使用完畢后未釋放,結果導致一直占據(jù)該內(nèi)存單元即為內(nèi)存泄露。

  • 1). 使用的時候要記得指針的長度.
  • 2). malloc的時候得確定在那里free.
  • 3). 對指針賦值的時候應該注意被賦值指針需要不需要釋放.
  • 4). 動態(tài)分配內(nèi)存的指針最好不要再次賦值.
  • 5). 在C++中應該優(yōu)先考慮使用智能指針.

第二部分:C v.s. C++

1. C和C++的區(qū)別?

  • 1). C++是C的超集;
  • 2). C是一個結構化語言,它的重點在于算法和數(shù)據(jù)結構。C程序的設計首要考慮的是如何通過一個過程,對輸入(或環(huán)境條件)進行運算處理得到輸出(或實現(xiàn)過程(事務)控制),而對于C++,首要考慮的是如何構造一個對象模型,讓這個模型能夠契合與之對應的問題域,這樣就可以通過獲取對象的狀態(tài)信息得到輸出或實現(xiàn)過程(事務)控制。

2. int fun() 和 int fun(void)的區(qū)別?

這里考察的是c 中的默認類型機制。

  • 在c中,int fun() 會解讀為返回值為int(即使前面沒有int,也是如此,但是在c++中如果沒有返回類型將報錯),輸入類型和個數(shù)沒有限制, 而int fun(void)則限制輸入類型為一個void。
  • 在c++下,這兩種情況都會解讀為返回int類型,輸入void類型。

3. const 有什么用途

主要有三點:

  • 1).定義只讀變量,或者常量(只讀變量和常量的區(qū)別參考下面一條);
  • 2).修飾函數(shù)的參數(shù)和函數(shù)的返回值;
  • 3).修飾函數(shù)的定義體,這里的函數(shù)為類的成員函數(shù),被const修飾的成員函數(shù)代表不能修改成員變量的值,因此const成員函數(shù)只能調用const成員函數(shù);
  • 4).只讀對象。只讀對象只能調用const成員函數(shù)。
class Screen { public: const char cha; //const成員變量 char get() const; //const成員函數(shù) };const Screen screen; //只讀對象
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4. 在C中用const 能定義真正意義上的常量嗎?C++中的const呢?

不能。c中的const僅僅是從編譯層來限定,不允許對const 變量進行賦值操作,在運行期是無效的,所以并非是真正的常量(比如通過指針對const變量是可以修改值的),但是c++中是有區(qū)別的,c++在編譯時會把const常量加入符號表,以后(仍然在編譯期)遇到這個變量會從符號表中查找,所以在C++中是不可能修改到const變量的。
補充:

  • 1). c中的局部const常量存儲在棧空間,全局const常量存在只讀存儲區(qū),所以全局const常量也是無法修改的,它是一個只讀變量。
  • 2). 這里需要說明的是,常量并非僅僅是不可修改,而是相對于變量,它的值在編譯期已經(jīng)決定,而不是在運行時決定。
  • 3).c++中的const 和宏定義是有區(qū)別的,宏是在預編譯期直接進行文本替換,而const發(fā)生在編譯期,是可以進行類型檢查和作用域檢查的。
  • 4).c語言中只有enum可以實現(xiàn)真正的常量。
  • 5). c++中只有用字面量初始化的const常量會被加入符號表,而變量初始化的const常量依然只是只讀變量。
  • 6). c++中const成員為只讀變量,可以通過指針修改const成員的值,另外const成員變量只能在初始化列表中進行初始化。

下面我們通過代碼來看看區(qū)別。
同樣一段代碼,在c編譯器下,打印結果為*pa = 4, 4
在c++編譯下打印的結果為 *pa = 4, 8

int main(void) {const int a = 8;int *pa = (int *)&a;*pa = 4;printf("*pa = %d, a = %d", *pa, a);return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

另外值得一說的是,由于c++中const常量的值在編譯期就已經(jīng)決定,下面的做法是OK的,但是c中是編譯通不過的。

int main(void) {const int a = 8;const int b = 2;int array[a+b] = {0};return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

5. 宏和內(nèi)聯(lián)(inline)函數(shù)的比較?

  • 1). 首先宏是C中引入的一種預處理功能;
  • 2). 內(nèi)聯(lián)(inline)函數(shù)是C++中引用的一個新的關鍵字;C++中推薦使用內(nèi)聯(lián)函數(shù)來替代宏代碼片段;
  • 3). 內(nèi)聯(lián)函數(shù)將函數(shù)體直接擴展到調用內(nèi)聯(lián)函數(shù)的地方,這樣減少了參數(shù)壓棧,跳轉,返回等過程;
  • 4). 由于內(nèi)聯(lián)發(fā)生在編譯階段,所以內(nèi)聯(lián)相較宏,是有參數(shù)檢查和返回值檢查的,因此使用起來更為安全;
  • 5). 需要注意的是, inline會向編譯期提出內(nèi)聯(lián)請求,但是是否內(nèi)聯(lián)由編譯期決定(當然可以通過設置編譯器,強制使用內(nèi)聯(lián));
  • 6). 由于內(nèi)聯(lián)是一種優(yōu)化方式,在某些情況下,即使沒有顯示的聲明內(nèi)聯(lián),比如定義在class內(nèi)部的方法,編譯器也可能將其作為內(nèi)聯(lián)函數(shù)。
  • 7). 內(nèi)聯(lián)函數(shù)不能過于復雜,最初C++限定不能有任何形式的循環(huán),不能有過多的條件判斷,不能對函數(shù)進行取地址操作等,但是現(xiàn)在的編譯器幾乎沒有什么限制,基本都可以實現(xiàn)內(nèi)聯(lián)。
    更多請參考inline關鍵字

6. C++中有了malloc / free , 為什么還需要 new / delete?

  • 1). malloc與free是C++/C語言的標準庫函數(shù),new/delete是C++的運算符。它們都可用于申請動態(tài)內(nèi)存和釋放內(nèi)存。
  • 2). 對于非內(nèi)部數(shù)據(jù)類型的對象而言,光用maloc/free無法滿足動態(tài)對象的要求。對象在創(chuàng)建的同時要自動執(zhí)行構造函數(shù),對象在消亡之前要自動執(zhí)行析構函數(shù)。
    由于malloc/free是庫函數(shù)而不是運算符,不在編譯器控制權限之內(nèi),不能夠把執(zhí)行構造函數(shù)和析構函數(shù)的任務強加于malloc/free。因此C++語言需要一個能完成動態(tài)內(nèi)存分配和初始化工作的運算符new,以一個能完成清理與釋放內(nèi)存工作的運算符delete。注意new/delete不是庫函數(shù)。
    最后補充一點體外話,new 在申請內(nèi)存的時候就可以初始化(如下代碼), 而malloc是不允許的。另外,由于malloc是庫函數(shù),需要相應的庫支持,因此某些簡易的平臺可能不支持,但是new就沒有這個問題了,因為new是C++語言所自帶的運算符。
int *p = new int(1);
  • 1

特別的,在C++中,如下的代碼,用new創(chuàng)建一個對象(new 會觸發(fā)構造函數(shù), delete會觸發(fā)析構函數(shù)),但是malloc僅僅申請了一個空間,所以在C++中引入new和delete來支持面向對象。

#include <cstdlib> class Test {... }Test* pn = new Test; Test* pm = (Test*)malloc(sizeof(Test));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

7. C和C++中的強制類型轉換?

C中是直接在變量或者表達式前面加上(小括號括起來的)目標類型來進行轉換,一招走天下,操作簡單,但是由于太過直接,缺少檢查,因此容易發(fā)生編譯檢查不到錯誤,而人工檢查又及其難以發(fā)現(xiàn)的情況;而C++中引入了下面四種轉換:

  • 1). static_cast
    a. 用于基本類型間的轉換
    b. 不能用于基本類型指針間的轉換
    c. 用于有繼承關系類對象間的轉換和類指針間的轉換
  • 2). dynamic_cast
    a. 用于有繼承關系的類指針間的轉換
    b. 用于有交叉關系的類指針間的轉換
    c. 具有類型檢查的功能
    d. 需要虛函數(shù)的支持
  • 3). reinterpret_cast
    a. 用于指針間的類型轉換
    b. 用于整數(shù)和指針間的類型轉換
  • 4). const_cast
    a. 用于去掉變量的const屬性
    b. 轉換的目標類型必須是指針或者引用
    在C++中,普通類型可以通過類型轉換構造函數(shù)轉換為類類型,那么類可以轉換為普通類型嗎?答案是肯定的。但是在工程應用中一般不用類型轉換函數(shù),因為無法抑制隱式的調用類型轉換函數(shù)(類型轉換構造函數(shù)可以通過explicit來抑制其被隱式的調用),而隱式調用經(jīng)常是bug的來源。實際工程中替代的方式是定義一個普通函數(shù),通過顯式的調用來達到類型轉換的目的。
class test{int m_value;... public:operator int() //類型轉換函數(shù){return m_value;}int toInt() //顯示調用普通函數(shù)來實現(xiàn)類型轉換{return m_value} };int main() {...test a(5);int i = a;...return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

8. static 有什么用途

  • 1). 靜態(tài)(局部/全局)變量
  • 2). 靜態(tài)函數(shù)
  • 3). 類的靜態(tài)數(shù)據(jù)成員
  • 4). 類的靜態(tài)成員函數(shù)

9. 類的靜態(tài)成員變量和靜態(tài)成員函數(shù)各有哪些特性?

靜態(tài)成員變量

  • 1). 靜態(tài)成員變量需要在類內(nèi)聲明(加static),在類外初始化(不能加static),如下例所示;
  • 2). 靜態(tài)成員變量在類外單獨分配存儲空間,位于全局數(shù)據(jù)區(qū),因此靜態(tài)成員變量的生命周期不依賴于類的某個對象,而是所有類的對象共享靜態(tài)成員變量;
  • 3). 可以通過對象名直接訪問公有靜態(tài)成員變量;
  • 4). 可以通過類名直接調用公有靜態(tài)成員變量,即不需要通過對象,這一點是普通成員變量所不具備的。
class example{ public: static int m_int; //static成員變量 };int example::m_int = 0; //沒有staticcout<<example::m_int; //可以直接通過類名調用靜態(tài)成員變量
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

靜態(tài)成員函數(shù)

  • 1). 靜態(tài)成員函數(shù)是類所共享的;
  • 2). 靜態(tài)成員函數(shù)可以訪問靜態(tài)成員變量,但是不能直接訪問普通成員變量(需要通過對象來訪問);需要注意的是普通成員函數(shù)既可以訪問普通成員變量,也可以訪問靜態(tài)成員變量;
  • 3). 可以通過對象名直接訪問公有靜態(tài)成員函數(shù);
  • 4). 可以通過類名直接調用公有靜態(tài)成員函數(shù),即不需要通過對象,這一點是普通成員函數(shù)所不具備的。
class example{ private: static int m_int_s; //static成員變量 int m_int; static int getI() //靜態(tài)成員函數(shù)在普通成員函數(shù)前加static即可 {return m_int_s; //如果返回m_int則報錯,但是可以return d.m_int是合法的 } };cout<<example::getI(); //可以直接通過類名調用靜態(tài)成員變量
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

10. 在C++程序中調用被C編譯器編譯后的函數(shù),為什么要加extern“C”?

C++語言支持函數(shù)重載,C語言不支持函數(shù)重載,函數(shù)被C++編譯器編譯后在庫中的名字與C語言的不同,假設某個函數(shù)原型為:

void foo(int x, int y);
  • 1

該函數(shù)被C編譯器編譯后在庫中的名字為 _foo, 而C++編譯器則會產(chǎn)生像: _foo_int_int 之類的名字。為了解決此類名字匹配的問題,C++提供了C鏈接交換指定符號 extern “C”。

11. 頭文件中的 ifndef/define/endif 是干什么用的? 該用法和 program once 的區(qū)別?

相同點:
它們的作用是防止頭文件被重復包含。
不同點

  • 1). ifndef 由語言本身提供支持,但是 program once 一般由編譯器提供支持,也就是說,有可能出現(xiàn)編譯器不支持的情況(主要是比較老的編譯器)。
  • 2). 通常運行速度上 ifndef 一般慢于 program once,特別是在大型項目上, 區(qū)別會比較明顯,所以越來越多的編譯器開始支持 program once。
  • 3). ifndef 作用于某一段被包含(define 和 endif 之間)的代碼, 而 program once 則是針對包含該語句的文件, 這也是為什么 program once 速度更快的原因。
  • 4). 如果用 ifndef 包含某一段宏定義,當這個宏名字出現(xiàn)“撞車”時,可能會出現(xiàn)這個宏在程序中提示宏未定義的情況(在編寫大型程序時特性需要注意,因為有很多程序員在同時寫代碼)。相反由于program once 針對整個文件, 因此它不存在宏名字“撞車”的情況, 但是如果某個頭文件被多次拷貝,program once 無法保證不被多次包含,因為program once 是從物理上判斷是不是同一個頭文件,而不是從內(nèi)容上。

12. 當i是一個整數(shù)的時候++i和i++那個更快一點?i++和++i的區(qū)別是什么?

答:理論上++i更快,實際與編譯器優(yōu)化有關,通常幾乎無差別。

//i++實現(xiàn)代碼為: int operator++(int) {int temp = *this;++*this;return temp; }//返回一個int型的對象本身// ++i實現(xiàn)代碼為: int& operator++() {*this += 1;return *this; }//返回一個int型的對象引用
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

i++和++i的考點比較多,簡單來說,就是i++返回的是i的值,而++i返回的是i+1的值。也就是++i是一個確定的值,是一個可修改的左值,如下使用:

cout << ++(++(++i)) << endl; cout << ++ ++i << endl;
  • 1
  • 2

可以不停的嵌套++i。
這里有很多的經(jīng)典筆試題,一起來觀摩下:

int main() {int i = 1;printf("%d,%d\n", ++i, ++i); //3,3printf("%d,%d\n", ++i, i++); //5,3printf("%d,%d\n", i++, i++); //6,5printf("%d,%d\n", i++, ++i); //8,9system("pause");return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

首先是函數(shù)的參數(shù)入棧順序從右向左入棧的,計算順序也是從右往左計算的,不過都是計算完以后再進行的壓棧操作:
對于第1個printf,首先執(zhí)行++i,返回值是i,這時i的值是2,再次執(zhí)行++i,返回值是i,得到i=3,將i壓入棧中,此時i為3,也就是壓入3,3;
對于第2個printf,首先執(zhí)行i++,返回值是原來的i,也就是3,再執(zhí)行++i,返回值是i,依次將3,5壓入棧中得到輸出結果
對于第3個printf,首先執(zhí)行i++,返回值是5,再執(zhí)行i++返回值是6,依次將5,6壓入棧中得到輸出結果
對于第4個printf,首先執(zhí)行++i,返回i,此時i為8,再執(zhí)行i++,返回值是8,此時i為9,依次將i,8也就是9,8壓入棧中,得到輸出結果。
上面的分析也是基于VS搞的,不過準確來說函數(shù)多個參數(shù)的計算順序是未定義的(the order of evaluation of function arguments are undefined)。筆試題目的運行結果隨不同的編譯器而異。

1. C中static有什么作用

  (1)隱藏。 當我們同時編譯多個文件時,所有未加static前綴的全局變量和函數(shù)都具有全局可見性,故使用static在不同的文件中定義同名函數(shù)和同名變量,而不必擔心命名沖突。

  (2)static的第二個作用是保持變量內(nèi)容的持久。存儲在靜態(tài)數(shù)據(jù)區(qū)的變量會在程序剛開始運行時就完成初始化,也是唯一的一次初始化。共有兩種變量存儲在靜態(tài)存儲區(qū):全局變量和static變量。

  (3)static的第三個作用是默認初始化為0.其實全局變量也具備這一屬性,因為全局變量也存儲在靜態(tài)數(shù)據(jù)區(qū)。在靜態(tài)數(shù)據(jù)區(qū),內(nèi)存中所有的字節(jié)默認值都是0×00,某些時候這一特點可以減少程序員的工作量。

2.C++中const有什么用?

  不要一聽到const就說是常量,這樣給考官一種在和一個外行交談的感覺。應該說const修飾的內(nèi)容不可改變就行了, 定義常量只是一種使用方式而已,還有const數(shù)據(jù)成員,const參數(shù), const返回值, const成員函數(shù)等, 被const修飾的東西都受到強制保護,可以預防意外的變動,能提高程序的健壯性。

3. C與C++各自是如何定義常量的?有什么不同?

  C中是使用宏#define定義, C++使用更好的const來定義。

  區(qū)別:

  1)const是有數(shù)據(jù)類型的常量,而宏常量沒有,編譯器可以對前者進行靜態(tài)類型安全檢查,對后者僅是字符替換,沒有類型安全檢查,而且在字符替換時可能會產(chǎn)生意料不到的錯誤(邊際效應)。

  2)有些編譯器可以對const常量進行調試, 不能對宏調試。

4. 既然C++中有更好的const為什么還要使用宏?

  const無法代替宏作為衛(wèi)哨來防止文件的重復包含。

5. C++中引用和指針的區(qū)別?

  引用是對象的別名, 操作引用就是操作這個對象, 必須在創(chuàng)建的同時有效得初始化(引用一個有效的對象, 不可為NULL), 初始化完畢就再也不可改變, 引用具有指針的效率, 又具有變量使用的方便性和直觀性, 在語言層面上引用和對象的用法一樣, 在二進制層面上引用一般都是通過指針來實現(xiàn)的, 只是編譯器幫我們完成了轉換。 之所以使用引用是為了用適當?shù)墓ぞ咦銮∪缙浞值氖?#xff0c; 體現(xiàn)了最小特權原則。

6. 說一說C與C++的內(nèi)存分配方式?

  1)從靜態(tài)存儲區(qū)域分配。內(nèi)存在程序編譯的時候就已經(jīng)分配好,這塊內(nèi)存在程序的整個運行期間都存在,如全局變量,static變量。

  2)在棧上創(chuàng)建。在執(zhí)行函數(shù)時,函數(shù)內(nèi)局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結束時這些存儲單元自動被釋放。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。

  3)從堆上分配(動態(tài)內(nèi)存分配)程序在運行的時候用malloc或new申請任意多少的內(nèi)存,程序員負責在何時用free或delete釋放內(nèi)存。動態(tài)內(nèi)存的生存期自己決定,使用非常靈活。

7. new/delete 與 malloc()/free() 的區(qū)別?

  malloc() 與 free() 是C語言的標準庫函數(shù), new/delete 是C++的運算符, 他們都可以用來申請和釋放內(nèi)存, malloc()和free()不在編譯器控制權限之內(nèi), 不能把構造函數(shù)和析構函數(shù)的任務強加給他們。www.cdtarena.com

8. #include<a.h>和#include"a.h" 有什么區(qū)別?

  答:對于#include <a.h> ,編譯器從標準庫路徑開始搜索 a.h對于#include "a.h" ,編譯器從用戶的工作路徑開始搜索 a.h

9. 在C++ 程序中調用被 C編譯器編譯后的函數(shù),為什么要加 extern “C”?

  C++語言支持函數(shù)重載,C語言不支持函數(shù)重載。函數(shù)被C++編譯后在庫中的名字與C語言的不同。假設某個函數(shù)的原型為: void foo(int x, int y);該函數(shù)被C編譯器編譯后在庫中的名字為_foo,而C++編譯器則會產(chǎn)生像_foo_int_int之類的名字。C++提供了C連接交換指定符號extern"C"來解決名字匹配問題。

10. C++中的什么是多態(tài)性? 是如何實現(xiàn)的?

  多態(tài)性是面向對象程序設計語言繼數(shù)據(jù)抽象和繼承之后的第三個基本特征。它是在運行時出現(xiàn)的多態(tài)性通過派生類和虛函數(shù)實現(xiàn)。基類和派生類中使用同樣的函數(shù)名, 完成不同的操作具體實現(xiàn)相隔離的另一類接口,即把" w h a t"從"h o w"分離開來。多態(tài)性提高了代碼的組織性和可讀性,虛函數(shù)則根據(jù)類型的不同來進行不同的隔離。

11. 什么是動態(tài)特性?

  在絕大多數(shù)情況下, 程序的功能是在編譯的時候就確定下來的, 我們稱之為靜態(tài)特性。 反之, 如果程序的功能是在運行時刻才能確定下來的, 則稱之為動態(tài)特性。C++中, 虛函數(shù),抽象基類, 動態(tài)綁定和多態(tài)構成了出色的動態(tài)特性。

12.什么是封裝?C++中是如何實現(xiàn)的?

  封裝來源于信息隱藏的設計理念, 是通過特性和行為的組合來創(chuàng)建新數(shù)據(jù)類型讓接口與具體實現(xiàn)相隔離。C++中是通過類來實現(xiàn)的, 為了盡量避免某個模塊的行為干擾同一系統(tǒng)中的其它模塊,應該讓模塊僅僅公開必須讓外界知道的接口。

13. 什么是RTTI?

  RTTI事指運行時類型識別(Run-time type identification)在只有一個指向基類的指針或引用時確定一個對象的準確類型。

14. 什么是拷貝構造函數(shù)?

  它是單個參數(shù)的構造函數(shù),其參數(shù)是與它同屬一類的對象的(常)引用;類定義中,如果未提供自己的拷貝構造函數(shù),C++提供一個默認拷貝構造函數(shù),該默認拷貝構造函數(shù)完成一個成員到一個成員的拷貝

15. 什么是深淺拷貝?

  淺拷貝是創(chuàng)建了一個對象用一個現(xiàn)成的對象初始化它的時候只是復制了成員(簡單賦值)而沒有拷貝分配給成員的資源(如給其指針變量成員分配了動態(tài)內(nèi)存); 深拷貝是當一個對象創(chuàng)建時,如果分配了資源,就需要定義自己的拷貝構造函數(shù),使之不但拷貝成員也拷貝分配給它的資源。

16.面向對象程序設計的優(yōu)點?

  開發(fā)時間短, 效率高, 可靠性高。面向對象編程的編碼具有高可重用性,可以在應用程序中大量采用成熟的類庫(如STL),從而雖短了開發(fā)時間,軟件易于維護和升級。

第三部分:數(shù)組、指針 & 引用

1. 指針和引用的區(qū)別?

相同點:

  • 1). 都是地址的概念;
  • 2). 都是“指向”一塊內(nèi)存。指針指向一塊內(nèi)存,它的內(nèi)容是所指內(nèi)存的地址;而引用則是某塊內(nèi)存的別名;
  • 3). 引用在內(nèi)部實現(xiàn)其實是借助指針來實現(xiàn)的,一些場合下引用可以替代指針,比如作為函數(shù)形參。
不同點:
  • 1). 指針是一個實體,而引用(看起來,這點很重要)僅是個別名;
  • 2). 引用只能在定義時被初始化一次,之后不可變;指針可變;引用“從一而終”,指針可以“見異思遷”;
  • 3). 引用不能為空,指針可以為空;
  • 4). “sizeof 引用”得到的是所指向的變量(對象)的大小,而“sizeof 指針”得到的是指針本身的大小;
  • 5). 指針和引用的自增(++)運算意義不一樣;
  • 6). 引用是類型安全的,而指針不是 (引用比指針多了類型檢查)
  • 7). 引用具有更好的可讀性和實用性。

2. 引用占用內(nèi)存空間嗎?

如下代碼中對引用取地址,其實是取的引用所對應的內(nèi)存空間的地址。這個現(xiàn)象讓人覺得引用好像并非一個實體。但是引用是占用內(nèi)存空間的,而且其占用的內(nèi)存和指針一樣,因為引用的內(nèi)部實現(xiàn)就是通過指針來完成的。

比如 Type& name; <===> Type* const name。

int main(void) {int a = 8;const int &b = a;int *p = &a;*p = 0;cout<<a; //output 0return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

3. 三目運算符

在C中三目運算符(? :)的結果僅僅可以作為右值,比如如下的做法在C編譯器下是會報錯的,但是C++中卻是可以是通過的。這個進步就是通過引用來實現(xiàn)的,因為下面的三目運算符的返回結果是一個引用,然后對引用進行賦值是允許的。

int main(void) {int a = 8;int b = 6;(a>b ? a : b) = 88;cout<<a; //output 88return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4. 指針數(shù)組和數(shù)組指針的區(qū)別

數(shù)組指針,是指向數(shù)組的指針,而指針數(shù)組則是指該數(shù)組的元素均為指針。

  • 數(shù)組指針,是指向數(shù)組的指針,其本質為指針,形式如下。如 int (*p)[10],p即為指向數(shù)組的指針,()優(yōu)先級高,首先說明p是一個指針,指向一個整型的一維數(shù)組,這個一維數(shù)組的長度是n,也可以說是p的步長。也就是說執(zhí)行p+1時,p要跨過n個整型數(shù)據(jù)的長度。數(shù)組指針是指向數(shù)組首元素的地址的指針,其本質為指針,可以看成是二級指針。
類型名 (*數(shù)組標識符)[數(shù)組長度]
  • 1
  • 指針數(shù)組,在C語言和C++中,數(shù)組元素全為指針的數(shù)組稱為指針數(shù)組,其中一維指針數(shù)組的定義形式如下。指針數(shù)組中每一個元素均為指針,其本質為數(shù)組。如 int *p[n], []優(yōu)先級高,先與p結合成為一個數(shù)組,再由int*說明這是一個整型指針數(shù)組,它有n個指針類型的數(shù)組元素。這里執(zhí)行p+1時,則p指向下一個數(shù)組元素,這樣賦值是錯誤的:p=a;因為p是個不可知的表示,只存在p[0]、p[1]、p[2]…p[n-1],而且它們分別是指針變量可以用來存放變量地址。但可以這樣 *p=a; 這里*p表示指針數(shù)組第一個元素的值,a的首地址的值。
類型名 *數(shù)組標識符[數(shù)組長度]
  • 1

5. 左值引用與右值引用

該部分主要摘自:c++ 學習筆記

左值引用就是我們通常所說的引用,如下所示。左值引用通常可以看作是變量的別名。

type-id & cast-expression // demo int a = 10 int &b = aint &c = 10 // 錯誤,無所對一個立即數(shù)做引用const int &d = 10 // 正確, 常引用引用常數(shù)量是ok的,其等價于 const int temp = 10; const int &d = temp
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

右值引用是 C++11 新增的特性,其形式如下所示。右值引用用來綁定到右值,綁定到右值以后本來會被銷毀的右值的生存期會延長至與綁定到它的右值引用的生存期。

type-id && cast-expression

// demo
int &&var = 10; // ok

int a = 10
int &&b = a // 錯誤, a 為左值

int &&c = var // 錯誤,var 為左值

int &&d = move(a) // ok, 通過move得到左值的右值引用

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在匯編層面右值引用做的事情和常引用是相同的,即產(chǎn)生臨時量來存儲常量。但是,唯一 一點的區(qū)別是,右值引用可以進行讀寫操作,而常引用只能進行讀操作。

6. 右值引用的意義

  • 右值引用支持移動語義的實現(xiàn),可以減少拷貝,提升程序的執(zhí)行效率。

    下面的代碼時沒有采用右值引用時的實現(xiàn)。

class Stack { public:// 構造Stack(int size = 1000) :msize(size), mtop(0){cout << "Stack(int)" << endl;mpstack = new int[size];}// 析構~Stack(){cout << "~Stack()" << endl;delete[]mpstack;mpstack = nullptr;}// 拷貝構造Stack(const Stack &src):msize(src.msize), mtop(src.mtop){cout << "Stack(const Stack&)" << endl;mpstack = new int[src.msize];for (int i = 0; i < mtop; ++i) {mpstack[i] = src.mpstack[i];}}// 賦值重載Stack& operator=(const Stack &src){cout << "operator=" << endl;if (this == &src)return *this;delete[]mpstack;msize = src.msize;mtop = src.mtop;mpstack = new int[src.msize];for (int i = 0; i < mtop; ++i) {mpstack[i] = src.mpstack[i];}return *this;}int getSize() {return msize;} private:int *mpstack;int mtop;int msize; };Stack GetStack(Stack &stack) {Stack tmp(stack.getSize());return tmp; }int main() {Stack s;s = GetStack(s);return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

運行結果如下。

Stack(int) // 構造s Stack(int) // 構造tmp Stack(const Stack&) // tmp拷貝構造main函數(shù)棧幀上的臨時對象 ~Stack() // tmp析構 operator= // 臨時對象賦值給s ~Stack() // 臨時對象析構 ~Stack() // s析構
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

執(zhí)行代碼的過程中調用拷貝構造,將內(nèi)存中的內(nèi)容逐個拷貝,在 C++ 11 中可以借助右值引用實現(xiàn)移動拷貝構造和移動賦值來解決這個問題。

Stack(Stack &&src):msize(src.msize), mtop(src.mtop) {cout << "Stack(Stack&&)" << endl;/*此處沒有重新開辟內(nèi)存拷貝數(shù)據(jù),把src的資源直接給當前對象,再把src置空*/mpstack = src.mpstack; src.mpstack = nullptr; }// 帶右值引用參數(shù)的賦值運算符重載函數(shù) Stack& operator=(Stack &&src) {cout << "operator=(Stack&&)" << endl;if(this == &src)return *this;delete[]mpstack;msize = src.msize;mtop = src.mtop;/*此處沒有重新開辟內(nèi)存拷貝數(shù)據(jù),把src的資源直接給當前對象,再把src置空*/mpstack = src.mpstack;src.mpstack = nullptr;return *this; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

執(zhí)行結果如下。可以看到,在有拷貝構造和移動拷貝構造函數(shù)的時候,優(yōu)先調用了移動拷貝構造和移動賦值。在移動拷貝構造和移動賦值中直接把資源所有權進行了轉移,而非拷貝,這就大大提高了執(zhí)行效率。

Stack(int) // 構造s Stack(int) // 構造tmp Stack(Stack&&) // 調用帶右值引用的拷貝構造函數(shù),直接將tmp的資源給臨時對象 ~Stack() // tmp析構 operator=(Stack&&) // 調用帶右值引用的賦值運算符重載函數(shù),直接將臨時對象資源給s ~Stack() // 臨時對象析構 ~Stack() // s析構
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 右值引用在可以使重載函數(shù)變得更加簡潔。右值引用可以適用 const T& 和 T& 形式的參數(shù)。

  • struct W { W(int&, int&) {} }; struct X { X(const int&, int&) {} }; struct Y { Y(int&, const int&) {} }; struct Z { Z(const int&, const int&) {} };template <typename T, typename A1, typename A2> T* factory(A1& a1, A2& a2) { return new T(a1, a2); } template <typename T, typename A1, typename A2> T* factory_new(A1&& a1, A2&& a2) { return new T(std::forward<A1>(a1), std::forward<A2>(a2)); } // demo int a = 2; int b = 2;W* c = factory<w>(a, b); // ok Z* d = factory<Z>(2, 2); // 錯誤,2 是右值W* pw = factory_new<W>(a, b); // ok X* px = factory_new<X>(2, b); // ok Y* py = factory_new<Y>(a, 2); // ok Z* e = factory_new<Z>(2, 2); // ok W* f = factory_new<W>(2, 2); // 錯誤,
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    更多相關內(nèi)容可以參考:c++——左值、右值、左值引用、右值引用

    第四部分:C++特性

    1. 什么是面向對象(OOP)?面向對象的意義?

    Object Oriented Programming, 面向對象是一種對現(xiàn)實世界理解和抽象的方法、思想,通過將需求要素轉化為對象進行問題處理的一種思想。其核心思想是數(shù)據(jù)抽象、繼承和動態(tài)綁定(多態(tài))。
    面向對象的意義在于:將日常生活中習慣的思維方式引入程序設計中;將需求中的概念直觀的映射到解決方案中;以模塊為中心構建可復用的軟件系統(tǒng);提高軟件產(chǎn)品的可維護性和可擴展性。

    2. 解釋下封裝、繼承和多態(tài)?

    • 1). 封裝
      封裝是實現(xiàn)面向對象程序設計的第一步,封裝就是將數(shù)據(jù)或函數(shù)等集合在一個個的單元中(我們稱之為類)。
      封裝的意義在于保護或者防止代碼(數(shù)據(jù))被我們無意中破壞。
      從封裝的角度看,public, private 和 protected 屬性的特點如下。

      • 不管那種屬性,內(nèi)類都是可以訪問的
      • public 是一種暴露的手段,比如暴露接口,類的對象可以訪問
      • private 是一種隱藏的手段,類的對象不能訪問
      • protected 成員:
        • 和 public 一樣可以被子類繼承
        • 和 private 一樣不能在類外被直接調用
        • 特例:在衍生類中可以通過衍生類對象訪問,如下代碼所示
    class Base { public: Base(){}; virtual ~Base(){}; protected: int int_pro; }; class A : public Base { public: A(){}; A(int da){int_pro = da;} // 通過 obj 對象直接訪問 protected 成員void Set(A &obj){obj.int_pro = 24;} void PrintPro(){cout << "The proteted data is " << int_pro <<endl;} };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 2). 繼承
      繼承主要實現(xiàn)重用代碼,節(jié)省開發(fā)時間。
      子類可以繼承父類的一些東西。

      • a.**公有繼承(public)**公有繼承的特點是基類的公有成員和保護成員作為派生類的成員時,它們都保持原有的狀態(tài)(基類的私有成員仍然是私有的,不能被這個派生類的子類所訪問)。
      • b.**私有繼承(private)**私有繼承的特點是基類的公有成員和保護成員都作為派生類的私有成員(并且不能被這個派生類的子類所訪問)。
      • c.**保護繼承(protected)**保護繼承的特點是基類的所有公有成員和保護成員都成為派生類的保護成員(并且只能被它的派生類成員函數(shù)或友元訪問,基類的私有成員仍然是私有的)。

      這里特別提一下虛繼承。虛繼承是解決C++多重繼承問題(其一,浪費存儲空間;第二,存在二義性問題)的一種手段。比如菱形繼承,典型的應用就是 iostream, 其繼承于 istream 和 ostream,而 istream 和 ostream 又繼承于 ios。

    3).多態(tài)
    多態(tài)是指通過基類的指針或者引用,在運行時動態(tài)調用實際綁定對象函數(shù)的行為。與之相對應的編譯時綁定函數(shù)稱為靜態(tài)綁定。多態(tài)是設計模式的基礎,多態(tài)是框架的基礎。

    3. 什么時候生成默認構造函數(shù)(無參構造函數(shù))?什么時候生成默認拷貝構造函數(shù)?什么是深拷貝?什么是淺拷貝?默認拷貝構造函數(shù)是哪種拷貝?什么時候用深拷貝?

    • 1). 沒有任何構造函數(shù)時,編譯器會自動生成默認構造函數(shù),也就是無參構造函數(shù);當類沒有拷貝構造函數(shù)時,會生成默認拷貝構造函數(shù)。
    • 2). 深拷貝是指拷貝后對象的邏輯狀態(tài)相同,而淺拷貝是指拷貝后對象的物理狀態(tài)相同;默認拷貝構造函數(shù)屬于淺拷貝。
    • 3). 當系統(tǒng)中有成員指代了系統(tǒng)中的資源時,需要深拷貝。比如指向了動態(tài)內(nèi)存空間,打開了外存中的文件或者使用了系統(tǒng)中的網(wǎng)絡接口等。如果不進行深拷貝,比如動態(tài)內(nèi)存空間,可能會出現(xiàn)多次被釋放的問題。是否需要定義拷貝構造函數(shù)的原則是,是類是否有成員調用了系統(tǒng)資源,如果定義拷貝構造函數(shù),一定是定義深拷貝,否則沒有意義。
      更多可以參考下面的代碼,比較容易混淆的是賦值操作符,其實區(qū)分很簡單,在出現(xiàn)等號的時候,如果有構造新的對象時調用的就是構造,不然就是賦值操作符。
    class A { public:A() {m = new int[4]{ 1,2,3,4 };std::cout << "constructor" << std::endl;}~A() {if (m != nullptr) {delete[] m;}}A(const A& a) {this->m = new int[4];memcpy(a.m, this->m, this->len * sizeof(int));std::cout << "copy constructor" << std::endl;}// 移動構造A(A&& a) : m(a.m) {a.m = nullptr; std::cout << "move constructor" << std::endl;}// 賦值操作符重載A& operator= (const A& a) {memcpy(a.m, this->m, this->len * sizeof(int));std::cout << "operator" << std::endl;return *this;} ? private:int len = 4;int* m = nullptr; }; ? A getA(A a) {return a; } ? int main(void) {A a; // constructA b = a; // copy constructA c(a); // copy constructA d; // constructd = a; // operate ?A e = getA(a); // construct, move construct ?return 0; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    4. 構造函數(shù)和析構函數(shù)的執(zhí)行順序?

    構造函數(shù)

    • 1). 首先調用父類的構造函數(shù);
    • 2). 調用成員變量的構造函數(shù);
    • 3). 調用類自身的構造函數(shù)。

    析構函數(shù)

    對于棧對象或者全局對象,調用順序與構造函數(shù)的調用順序剛好相反,也即后構造的先析構。對于堆對象,析構順序與delete的順序相關。

    5. 虛析構函數(shù)的作用?

    基類采用虛析構函數(shù)可以防止內(nèi)存泄漏。比如下面的代碼中,如果基類 A 中不是虛析構函數(shù),則 B 的析構函數(shù)不會被調用,因此會造成內(nèi)存泄漏。

    class A{ public:A(){}//~A(){}virtual ~A(){} // 虛析構 }; class B : public A{ public:B(){// new memory}~B(){// delete memory} }; int main(int argc, char *argv) {A *p = new B;// some operations// ...delete p; // 由于基類中是虛析構,這里會先調用B的析構函數(shù),然后調用A的析構函數(shù)return 0; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    但并不是要把所有類的析構函數(shù)都寫成虛函數(shù)。因為當類里面有虛函數(shù)的時候,編譯器會給類添加一個虛函數(shù)表,里面來存放虛函數(shù)指針,這樣就會增加類的存儲空間。所以,只有當一個類被用來作為基類的時候,才把析構函數(shù)寫成虛函數(shù)。

    6. 細看拷貝構造函數(shù)

    對于 class A,它的拷貝構造函數(shù)如下:

    A::A(const A &a){}
    • 1

    1) 為什么必須是當前類的引用呢?

    循環(huán)調用。如果拷貝構造函數(shù)的參數(shù)不是當前類的引用,而是當前類的對象,那么在調用拷貝構造函數(shù)時,會將另外一個對象直接傳遞給形參,這本身就是一次拷貝,會再次調用拷貝構造函數(shù),然后又將一個對象直接傳遞給了形參,將繼續(xù)調用拷貝構造函數(shù)……這個過程會一直持續(xù)下去,沒有盡頭,陷入死循環(huán)。

    只有當參數(shù)是當前類的引用時,才不會導致再次調用拷貝構造函數(shù),這不僅是邏輯上的要求,也是 C++ 語法的要求。

    2) 為什么是 const 引用呢?

    拷貝構造函數(shù)的目的是用其它對象的數(shù)據(jù)來初始化當前對象,并沒有期望更改其它對象的數(shù)據(jù),添加 const 限制后,這個含義更加明確了。

    另外一個原因是,添加 const 限制后,可以將 const 對象和非 const 對象傳遞給形參了,因為非 const 類型可以轉換為 const 類型。如果沒有 const 限制,就不能將 const 對象傳遞給形參,因為 const 類型不能轉換為非 const 類型,這就意味著,不能使用 const 對象來初始化當前對象了。

    7. C++的編譯環(huán)境

    如下圖所示,C++的編譯環(huán)境由如下幾部分構成:C++標準庫、C語言兼容庫、編譯器擴展庫及編譯模塊。

    #include<iostream> //C++標準庫,不帶".h" #include<string.h> //C語言兼容庫,由編譯器廠商提供
    • 1
    • 2

    值得注意的是,C語言兼容庫功能上跟C++標準庫中的C語言子庫相同,它的存中主要為了兼容C語言編譯器,也就是說如果一個文件只包含C語言兼容庫(不包含C++標準庫),那么它在C語言編譯器中依然可以編譯通過。

    8. Most vexing parse

    直接上代碼吧。下面 f 和 g 是有問題的,這種情況就稱為 Most vexing parse。

    class A { public:A() { cout << "const without param" << endl; }A(int a) { cout << "const with param" << endl; }A(const A& b) { cout << "copy construct" << endl; } };int main(void) {A a; // const without paramA b(10); // const with paramA c = A(); // const without paramA d = A(10); // const with paramA e(d); // copy constructA f(); A g(A());A h{}; // const without paramA i{A{}}; // const without paramreturn 0; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    問題在哪?

    A f(); // 這個是不是可以看做聲明了一個返回值為A的函數(shù),函數(shù)名為 f,參數(shù)無 A g(A()); // 這個是不是可以看做聲明了一個返回值為A的函數(shù),函數(shù)名為 g, 參數(shù)類型為函數(shù)指針,這個函數(shù)指針的返回值類型為A,參數(shù)無
    • 1
    • 2

    解決辦法參考上面的 h, j。

總結

以上是生活随笔為你收集整理的面经——C/C++常见面试知识点总结附面试真题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。