秋招總結------C++面試題總結四

1. C++中類成員的訪問權限和繼承權限問題

  • 三種訪問權限
  1. public:用該關鍵字修飾的成員表示公有成員,該成員不僅可以在類內可以被  訪問,在類外也是可以被訪問的,是類對外提供的可訪問接口;
  2.  private:用該關鍵字修飾的成員表示私有成員,該成員僅在類內可以被訪問,在類體外是隱藏狀態;
  3.  protected:用該關鍵字修飾的成員表示保護成員,保護成員在類體外同樣是隱藏狀態,但是對於該類的派生類來說,相當於公有成員,在派生類中可以被訪問。
  • 三種繼承方式
  1. 若繼承方式是public,基類成員在派生類中的訪問權限保持不變,也就是說,基類中的成員訪問權限,在派生類中仍然保持原來的訪問權限;
  2.  若繼承方式是private,基類所有成員在派生類中的訪問權限都會變爲私有(private)權限;
  3. 若繼承方式是protected,基類的共有成員和保護成員在派生類中的訪問權限都會變爲保護(protected)權限,私有成員在派生類中的訪問權限仍然是私有(private)權限。

2. cout和printf有什麼區別?

  1. cout<<是一個函數,cout<<後可以跟不同的類型是因爲cout<<已存在針對各種類型數據的重載,所以會自動識別數據的類型。輸出過程會首先將輸出字符放入緩衝區,然後輸出到屏幕。

        cout是有緩衝輸出:

       cout < < "abc " < <endl;   或cout < < "abc\n ";cout < <flush; 這兩個纔是一樣的.  endl相當於輸出回車後,再強迫緩衝輸出。
       flush立即強迫緩衝輸出。

     2. printf是無緩衝輸出。有輸出時立即輸出

3. 重載運算符?

 

        類屬關係運算符"."、成員指針運算符".*"、作用域分辨符"::"、sizeof運算符和三目運算符"?:"

 

  1. 我們只能重載已有的運算符,而無權發明新的運算符;對於一個重載的運算符,其優先級和結合律與內置類型一致纔可以;不能改變運算符操作數個數
  2. .    ::   ?:  sizeof   typeid  **不能重載;
  3. 兩種重載方式,成員運算符和非成員運算符,成員運算符比非成員運算符少一個參數;下標運算符、箭頭運算符必須是成員運算符;
  4. 引入運算符重載,是爲了實現類的多態性
  5. 當重載的運算符是成員函數時,this綁定到左側運算符對象。成員運算符函數的參數數量比運算符對象的數量少一個;至少含有一個類類型的參數;
  6. 從參數的個數推斷到底定義的是哪種運算符,當運算符既是一元運算符又是二元運算符(+,-,*,&);
  7. 下標運算符必須是成員函數,下標運算符通常以所訪問元素的引用作爲返回值,同時最好定義下標運算符的常量版本和非常量版本;
  8. 箭頭運算符必須是類的成員,解引用通常也是類的成員;重載的箭頭運算符必須返回類的指針;

4. 定義和聲明的區別

  1. 如果是指變量的聲明和定義
    編譯原理上來說,聲明是僅僅告訴編譯器,有個某類型的變量會被使用,但是編譯器並不會爲它分配任何內存。而定義就是分配了內存
  2. 如果是指函數的聲明和定義
    聲明:一般在頭文件裏,對編譯器說:這裏我有一個函數叫function() 讓編譯器知道這個函數的存在。
    定義:一般在源文件裏,具體就是函數的實現過程 寫明函數體。

5. C++類型轉換有四種

  • static_cast能進行基礎類型之間的轉換,也是最常看到的類型轉換。它主要有如下幾種用法:
  1. 1 . 用於類層次結構中父類和子類之間指針或引用的轉換。進行上行轉換(把子類的指針或引用轉換成父類表示)是安全的;

    2 . 進行下行轉換(把父類指針或引用轉換成子類指針或引用)時,由於沒有動態類型檢查,所以是不安全的;

    3 . 用於基本數據類型之間的轉換,如把int轉換成char,把int轉換成enum。這種轉換的安全性也要開發人員來保證。

    4 . 把void指針轉換成目標類型的指針(不安全!!)

    5 . 把任何類型的表達式轉換成void類型。

  2. const_cast運算符用來修改類型的const或volatile屬性。除了去掉const 或volatile修飾之外, type_id和expression得到的類型是一樣的。但需要特別注意的是const_cast不是用於去除變量的常量性,而是去除指向常數對象的指針或引用的常量性,其去除常量性的對象必須爲指針或引用。
  3. reinterpret_cast它可以把一個指針轉換成一個整數,也可以把一個整數轉換成一個指針(先把一個指針轉換成一個整數,在把該整數轉換成原類型的指針,還可以得到原先的指針值)。
  4. dynamic_cast 主要用在繼承體系中的安全向下轉型。它能安全地將指向基類的指針轉型爲指向子類的指針或引用,並獲知轉型動作成功是否。轉型失敗會返回null(轉型對象爲指針時)或拋出異常bad_cast(轉型對象爲引用時)。 dynamic_cast 會動用運行時信息(RTTI)來進行類型安全檢查,因此 dynamic_cast 存在一定的效率損失。當使用dynamic_cast時,該類型必須含有虛函數,這是因爲dynamic_cast使用了存儲在VTABLE中的信息來判斷實際的類型,RTTI運行時類型識別用於判斷類型。typeid表達式的形式是typeid(e),typeid操作的結果是一個常量對象的引用,該對象的類型是type_info或type_info的派生。

6. 靜態成員與普通成員的區別

  1. 生命週期:靜態成員變量從類被加載開始到類被卸載,一直存在;普通成員變量只有在類創建對象後纔開始存在,對象結束,它的生命期結束;
  2. 共享方式靜態成員變量是全類共享;普通成員變量是每個對象單獨享用的;
  3. 定義位置普通成員變量存儲在棧或堆中,而靜態成員變量存儲在靜態全局區;
  4. 初始化位置普通成員變量在類中初始化;靜態成員變量在類外初始化;
  5. 默認實參可以使用靜態成員變量作爲默認實參,

7. 多繼承的優缺點,作爲一個開發者怎麼看待多繼承

  1. C++允許爲一個派生類指定多個基類,這樣的繼承結構被稱做多重繼承。
  2. 多重繼承的優點很明顯,就是對象可以調用多個基類中的接口
  3. 如果派生類所繼承的多個基類有相同的基類,而派生類對象需要調用這個祖先類的接口方法,就會容易出現二義性
  4. 加上全局符確定調用哪一份拷貝。比如pa.Author::eat()調用屬於Author的拷貝。
  5. 使用虛擬繼承,使得多重繼承類Programmer_Author只擁有Person類的一份拷貝。

8. 迭代器++it,it++哪個好,爲什麼

  1. 前置返回一個引用,後置返回一個對象
  • // ++i實現代碼爲:

    int& operator++()

    {

        *this += 1;

        return *this;

    }

     

  1. 前置不會產生臨時對象,後置必須產生臨時對象,臨時對象會導致效率降低
  • //i++實現代碼爲:                                 

    int operator++(int)                                 

    {

    int temp = *this;                                    

           ++*this;                                            

           return temp;                                    

    }

9. 模板和實現可不可以不寫在一個文件裏面?爲什麼?

  1. 因爲在編譯時模板並不能生成真正的二進制代碼,而是在編譯調用模板類或函數的CPP文件時纔會去找對應的模板聲明和實現,在這種情況下編譯器是不知道實現模板類或函數的CPP文件的存在,所以它只能找到模板類或函數的聲明而找不到實現,而只好創建一個符號寄希望於鏈接程序找地址。但模板類或函數的實現並不能被編譯成二進制代碼,結果鏈接程序找不到地址只好報錯了。
  2. 《C++編程思想》第15章(第300頁)說明了原因:模板定義很特殊。由template<…>處理的任何東西都意味着編譯器在當時不爲它分配存儲空間,它一直處於等待狀態直到被一個模板實例告知。在編譯器和連接器的某一處,有一機制能去掉指定模板的多重定義。所以爲了容易使用,幾乎總是在頭文件中放置全部的模板聲明和定義。

10. ​​​​​​​class、union、struct的區別?

  1. C語言中,struct只是一個聚合數據類型沒有權限設置,無法添加成員函數,無法實現面向對象編程,且如果沒有typedef結構名,聲明結構變量必須添加關鍵字struct。
  2. C++中,struct功能大大擴展,可以有權限設置(默認權限爲public),可以像class一樣有成員函數,繼承(默認public繼承),可以實現面對對象編程,允許在聲明結構變量時省略關鍵字struct。
  3. C與C++中的union:一種數據格式,能夠存儲不同的數據類型,但只能同時存儲其中的一種類型。C++ union結構式一種特殊的類。它能夠包含訪問權限、成員變量、成員函數(可以包含構造函數和析構函數)。它不能包含虛函數和靜態數據變量。它也不能被用作其他類的基類,它本身也不能有從某個基類派生而來。Union中得默認訪問權限是public。union類型是共享內存的,以size最大的結構作爲自己的大小。每個數據成員在內存中的起始地址是相同的。
  4. 在C/C++程序的編寫中,當多個基本數據類型或複合數據結構要佔用同一片內存時,我們要使用聯合體;當多種類型,多個對象,多個事物只取其一時(我們姑且通俗地稱其爲“n 選1”),我們也可以使用聯合體來發揮其長處。在某一時刻,一個union中只能有一個值是有效的。union的一個用法就是可以用來測試CPU是大端模式還是小端模式。

11. ​​​​​​​動態聯編與靜態聯編

  1. 在C++中,聯編是指一個計算機程序不同部分彼此關聯的過程。按照聯編所進行的階段不同,可以分爲靜態聯編和動態聯編;
  2. 靜態聯編是指聯編工作在編譯階段完成的,這種聯編過程是在程序運行之前完成的,又稱爲早期聯編。要實現靜態聯編,在編譯階段就必須確定程序中的操作調用(如函數調用)與執行該操作代碼間的關係,確定這種關係稱爲束定,在編譯時的束定稱爲靜態束定。靜態聯編對函數的選擇是基於指向對象的指針或者引用的類型。其優點是效率高,但靈活性差。
  3. 動態聯編是指聯編在程序運行時動態地進行,根據當時的情況來確定調用哪個同名函數,實際上是在運行時虛函數的實現。這種聯編又稱爲晚期聯編,或動態束定。動態聯編對成員函數的選擇是基於對象的類型,針對不同的對象類型將做出不同的編譯結果。C++中一般情況下的聯編是靜態聯編,但是當涉及到多態性和虛函數時應該使用動態聯編。動態聯編的優點是靈活性強,但效率低。動態聯編規定,只能通過指向基類的指針或基類對象的引用來調用虛函數,其格式爲:指向基類的指針變量名->虛函數名(實參表)或基類對象的引用名.虛函數名(實參表)
  4. 實現動態聯編三個條件:
  5. 必須把動態聯編的行爲定義爲類的虛函數;
  6. 類之間應滿足子類型關係,通常表現爲一個類從另一個類公有派生而來;
  7.  必須先使用基類指針指向子類型的對象,然後直接或間接使用基類指針調用虛函數;

12. ​​​​​​​動態編譯與靜態編譯

  1. 靜態編譯,編譯器在編譯可執行文件時,把需要用到的對應動態鏈接庫中的部分提取出來,連接到可執行文件中去,使可執行文件在運行時不需要依賴於動態鏈接庫
  2. 動態編譯的可執行文件需要附帶一個動態鏈接庫,在執行時,需要調用其對應動態鏈接庫的命令。所以其優點一方面是縮小了執行文件本身的體積,另一方面是加快了編譯速度,節省了系統資源。缺點是哪怕是很簡單的程序,只用到了鏈接庫的一兩條命令,也需要附帶一個相對龐大的鏈接庫;二是如果其他計算機上沒有安裝對應的運行庫,則用動態編譯的可執行文件就不能運行。

13. ​​​​​​​講講大端小端,如何檢測(三種方法)

  1. 大端模式:是指數據的高字節保存在內存的低地址中,而數據的低字節保存在內存的高地址端。

  2. 小端模式,是指數據的高字節保存在內存的高地址中,低位字節保存在在內存的低地址端。

  3. 直接讀取存放在內存中的十六進制數值,取低位進行值判斷
  4. int a = 0x12345678;

    int *c = &a;

    c[0] == 0x12   大端模式

    c[0] == 0x78   小段模式

  5. 用共同體來進行判斷​​​​​​​
  6. union共同體所有數據成員是共享一段內存的,後寫入的成員數據將覆蓋之前的成員數據,成員數據都有相同的首地址。Union的大小爲最大數據成員的大小。

    union的成員數據共用內存,並且首地址都是低地址首字節。Int i= 1時:大端存儲1放在最高位,小端存儲1放在最低位。當讀取char ch時,是最低地址首字節,大小端會顯示不同的值。

    union w                             w p;

    {                               p.i = 1;

    int i;                         if(ch == 1)    

    char ch;}

14. ​​​​​​​查看內存的方法

  1. 首先打開vs編譯器,創建好項目,並且將代碼寫進去,這裏就不貼代碼了,你可以隨便的寫個做個測試;
  2. 調試的時候做好相應的斷點,然後點擊開始調試;
  3. 程序調試之後會在你設置斷點的地方暫停,然後選擇調試->窗口->內存,就打開了內存數據查看的窗口了。

15. ​​​​​​​空類會默認添加哪些東西?怎麼寫?

  1. Empty(); // 缺省構造函數//
  2. Empty( const Empty& ); // 拷貝構造函數//
  3. ~Empty(); // 析構函數//
  4. Empty& operator=( const Empty& ); // 賦值運算符//

16. ​​​​​​​ 爲什麼拷貝構造函數必須傳引用不能傳值?

  1. 拷貝構造函數的作用就是用來複製對象的,在使用這個對象的實例來初始化這個對象的一個新的實例。
    2) 參數傳遞過程到底發生了什麼?
      將地址傳遞和值傳遞統一起來,歸根結底還是傳遞的是"值"(地址也是值,只不過通過它可以找到另一個值)!
    i)值傳遞:
      對於內置數據類型的傳遞時,直接賦值拷貝給形參(注意形參是函數內局部變量);
      對於類類型的傳遞時,需要首先調用該類的拷貝構造函數來初始化形參(局部對象);如void foo(class_type obj_local){}, 如果調用foo(obj);  首先class_type obj_local(obj) ,這樣就定義了局部變量obj_local供函數內部使用
    ii)引用傳遞:
        無論對內置類型還是類類型,傳遞引用或指針最終都是傳遞的地址值!而地址總是指針類型(屬於簡單類型), 顯然參數傳遞時,按簡單類型的賦值拷貝,而不會有拷貝構造函數的調用(對於類類型).
    上述1) 2)回答了爲什麼拷貝構造函數使用值傳遞會產生無限遞歸調用,內存溢出。

    拷貝構造函數用來初始化一個非引用類類型對象,如果用傳值的方式進行傳參數,那麼構造實參需要調用拷貝構造函數,而拷貝構造函數需要傳遞實參,所以會一直遞歸

17. ​​​​​​​空類的大小是多少?爲什麼?

  1. C++空類的大小不爲0,不同編譯器設置不一樣,vs設置爲1;
  2. C++標準指出,不允許一個對象(當然包括類對象)的大小爲0,不同的對象不能具有相同的地址
  3. 帶有虛函數的C++類大小不爲1,因爲每一個對象會有一個vptr指向虛函數表,具體大小根據指針大小確定;
  4. C++中要求對於類的每個實例都必須有獨一無二的地址,那麼編譯器自動爲空類分配一個字節大小,這樣便保證了每個實例均有獨一無二的內存地址。

18. ​​​​​​​你什麼情況用指針當參數,什麼時候用引用,爲什麼?

  1. 使用引用參數的主要原因有兩個:
  • 程序員能修改調用函數中的數據對象

    通過傳遞引用而不是整個數據–對象,可以提高程序的運行速度 

  1. 一般的原則: 
    對於使用引用的值而不做修改的函數:
  • 如果數據對象很小,如內置數據類型或者小型結構,則按照值傳遞;

    如果數據對象是數組,則使用指針(唯一的選擇),並且指針聲明爲指向const的指針;

    如果數據對象是較大的結構,則使用const指針或者引用,已提高程序的效率。這樣可以節省結構所需的時間和空間;

    如果數據對象是類對象,則使用const引用(傳遞類對象參數的標準方式是按照引用傳遞);

  1. 對於修改函數中數據的函數:
  • 如果數據是內置數據類型,則使用指針

    如果數據對象是數組,則只能使用指針

    如果數據對象是結構,則使用引用或者指針

    如果數據是類對象,則使用引用

19. ​​​​​​​大內存申請時候選用哪種?C++變量存在哪?變量的大小存在哪?符號表存在哪?

  1. 大內存申請時,採用堆申請空間,用new申請;
  2. 不同的變量存儲在不同的地方,局部變量、全局變量、靜態變量;
  3. C++對變量名不作存儲,在彙編以後不會出現變量名,變量名作用只是用於方便編譯成彙編代碼,是給編譯器看的,是方便人閱讀的

20. ​​​​​​​靜態函數能定義爲虛函數嗎?常函數?

  1. static成員不屬於任何類對象或類實例,所以即使給此函數加上virutal也是沒有任何意義的。
  2. 靜態與非靜態成員函數之間有一個主要的區別。那就是靜態成員函數沒有this指針。虛函數依靠vptr和vtable來處理。vptr是一個指針,在類的構造函數中創建生成,並且只能用this指針來訪問它,因爲它是類的一個成員,並且vptr指向保存虛函數地址的vtable.對於靜態成員函數,它沒有this指針,所以無法訪問vptr. 這就是爲何static函數不能爲virtual.虛函數的調用關係:this -> vptr -> vtable ->virtual function

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章