C++基礎知識面試必備、複習細節 (1)

C++基礎知識面試必備、複習細節 (1)

c++變量與基本類型

(解決面試時常問的考點以及易忘點易混點)

一些經驗準則:
  • 如果明確數值不爲負,則選擇無符號類型
  • 使用int執行整數運算(如果超出int數值範圍則採用 long long)
  • 執行浮點數運算採用double(注意double類型不可以用==判斷相等,可以用相差值小於一個很小的值判斷)
  • 類型轉換時要注意範圍,浮點轉換到整型會損失小數部分
  • 切勿混用帶符號和無符號類型。(當無符號和有符號相加時,帶符號數會轉變爲無符號數,會引發錯誤)
  • 定義變量時儘量初始化,尤其在函數體內,因爲在函數體內的內置類型的變量如果沒有初始化其值是未定義
引用和指針
  • 引用是對象的一個別名,指針本身就是一個對象
  • 引用必須在定義時進行初始化,而指針無需(但爲了避免未知錯誤,通常定義指針時賦初值)
  • 指針的值在初始化後可以改變,即指向其它的存儲單元,而引用在進行初始化後就不會再改變了
  • ”sizeof引用”得到的是所指向的變量(對象)的大小,而”sizeof指針”得到的是指針本身的大小;
  • 將引用作爲函數的參數時,實際上就是直接將該變量傳入了函數,而不是再次拷貝了一個臨時變量,所以對該參數的修改將直接修改的原始的值。
const限定符
  • 爲了避免對值的修改,可以採用const限定符。注意:const對象必須初始化

    const int a; //錯,必須初始化
    const int b=10; //對,編譯時初始化
    
  • const引用:對常量的引用,對常量的引用不能被用作修改它綁定的對象

    const int ci=1024;
    const int &ri=ci; //正確,引用以及其對應的對象都是常量
    int &r2=ci;  //錯誤,試圖讓一個非常量引用指向一個常量對象
    
  • const指針

    • 指向常量的指針: const int * 表示的是一個指向const int的指針

      const int a=3;
      const int *p=&a; //正確,a是一個int常量,p是指向int常量的指針
      
    • 常量指針: int *const 表示的是指向int的指針,但其不能被修改,不變的是指針本身而不是那個值

      故 const int * const就是一個指向const int的常量指針

      int a=0;
      int *const p=&a; //p是一直指向a的指針
      const int b=0;
      const int *const p2=&b; //p2是指向常量對象的常量指針
      
處理類型
  • 採用關鍵字 typedef 創建易懂的變量別名

    typedef double wages; //wages將是double的同義詞
    wages a; //a是double類型
    
  • auto類型,可以讓編譯器自動分析表達式類型

    auto item=val1+val2; //如果val1和val2是int,則item將是int
    vector<int> array;
    for(auto i:array) //自動遍歷array中的每一個元素
    { }
    
自定義數據結構
  • struct 結構體

    struct Sales_date{
    	string book_name;
    	unsigned int prices=0;
    }; //注意加分號
    
標準庫類型string

可變長的字符串

  • 初始化

    string s1; //默認初始化
    string s2="hello"; //s2初始化爲"hellp"
    string s3(10,'a');  //s3的內容是"aaaaaaaaaa"
    
  • 一些常用操作:

    s.empty(); //s爲空則返回true,否則返回false
    s.size();  //返回字符串長度   s.length()
    s1+s2  ; //返回s1和s2連接後的字符串
    s1 < > <= >=  s2  //返回的是字典序比較,逐一比較的結果
    find (string s, size_t pos)//在當前字符串的pos索引位置開始,查找子串s,返回找到的位置索引
    
標準庫類型vector

vector是一個對象的集合,其中所有對象的類型都相同。容器

  • 初始化

    vector<T> v1;
    vector<int> a{1,2,3,4,5};
    vector<int> array(10); //表示array長度爲10,都初始化爲0
    vector<int> d(10,1);  //初始化爲10個1
    int array[]={1,2,3,4,5,6,7}
    vector<int> ve_array(begin(array),end(array)); //用數組初始化vector
    
  • 常用操作

    //添加元素  push_back()
    v1.push_back(5); //向尾部加入5
    //刪除元素  pop_back()
    v1.pop_back() 
    //判斷是否爲空
    v1.empty()
    //vector的長度
    v1.size()
    //遍歷vector
    for(auto i:v1)
        something();
    for(auto i=v1.begin();i<v1.end();i++)
        something();
    //查找某一元素
    v1.find()  //如果找到則返回對應下標,否則返回的是v1.end()
    
數組名和指針的區別與聯繫
  • 數組名存的就是數組第一個元素的地址,即 array==&array[0]; 同樣,可以令指針指向該數組,則指針所存內容也是數組第一個元素的地址,即可以將數組名賦值給指針
  • 數組名是不可以修改的,而指針是可以移動的,可以用指針遍歷一個數組
  • array+1表示array[1]的地址
  • sizeof(array)=sizeof(T)*length 即sizeof一個數組返回的是整個數組佔用的空間,而sizeof指針返回的是一個指針的空間

C++表達式

邏輯運算符求值的短路求值
  • 對於邏輯與運算符,當且僅當左側運算對象爲真時纔對右側運算對象求值。也就是說,如果左側爲假,則直接返回false而不繼續判斷右側

    index<array.size()&&array[index]>0  //通過該方式避免越界
    
  • 對於邏輯或運算符,當且僅當左側運算符爲假的時纔對右側對象求值。也就是說,如果左側爲真,則表達式爲true已經確定直接返回,而不繼續判斷

    if(s.empty()||s[s.size()-1]=='.') //s爲空或者以句號結尾則換行
    	cout<<endl;
    
遞增遞減運算符
  • 前置和後置的遞增運算符:

    • 前置版本:首先將運算對象加1,然後將改變後的對象返回
    • 後置版本:將運算對象加1,返回運算對象改變前的值的副本
    int i=0,j; 
    j=++i; //j=1,i=1
    j=i++; //j=1,i=2
    
  • 通常儘量使用前置版本,如果爲了賦值然後遍歷,則通常採用後置版本

    array[i++]=k; //令array[i]=k且向後遍歷一步
    
位運算符
  • 按位與 & ,將參與運算的兩個分量對應的每一位來做邏輯與運算,若兩者都爲真(等於1),則結果才爲真(等於1)。否則爲假(等於0 ) 即:1 & 1 = 1 、1&0 = 0 、0&1 = 1、0&0 = 0

  • 按位或 | , 將參與運算的每個分量對應的每一位來做邏輯或運算,即兩者都爲假(爲0)時,才爲假(爲0),否則爲真。即:0|0 = 0、1|0 = 1、0|1 = 1、1|1 = 1

  • 按位異或 ^ , 把參與運算的每個分量對應的每一位來做異或運算,即兩者相同爲假,不同爲真 即:0|0 = 0、 1|0 = 1、0|1 = 1、 1|1 = 0

  • 按位取反 ~ , 把二進制位的每一位進行取反運算,將二進制中1變成0,0變成1

  • 按位右移 >> 把二進制位整體向右移動。 7>>1 = 0000 0111 >> 1 = 0000 0011 = 3

    右移等於除了2的N次方,N爲右移的位數。

  • 按位左移 << 把二進制位整體向左移動。

sizeof運算符

sizeof運算符返回一條表達式或一個類型名字所佔的字節數。

  • 對類型爲引用的對象執行sizeof運算將得到被引用對象所佔空間的大小

  • 對類型爲指針的對象執行sizeof運算將得到指針本身所佔空間的大小

  • 對數組執行sizeof運算將得到整個數組所佔空間大小,等價於對數組每個元素sizeof然後求和

  • 對string和vector類執行sizeof運算只會返回該類型固定部分的大小而不是佔用空間的大小

  • 結構體、union、類爲空時,對其sizeof返回爲1

  • 靜態變量在sizeof計算時不需要考慮

  • 對結構體進行sizeof不是單純的加法,要注意對齊效果,即每個變量起始必須是自己所佔字節的倍數。結構體的大小必須是其中最大寬度的類型大小的整數倍,例子:

    struct test{
    	char a;  //1
    	int c;   //4
    }; 
    cout<<sizeof(test);  //是8而不是5,因爲int跳過char後面3個空間再放置,對齊
    
    struct test{
    	char a;  //1
    	int *p;  //8
    	char b;  //1
    }; 
    cout<<sizeof(test); //是24而不是10,首先p需要對齊所以需要到8的倍數處纔可以放置,然後放置b後,結構體的大小需要是p的整數倍,故最終爲24
    struct test
    {	
    }; 
    cout<<sizeof(test);  //是1而不是0,哪怕是空結構體也佔1字節
    
  • 對union對象進行sizeof時,結果爲其中最寬的數據類型的長度

    union test{
    	char a;
    	int p;
    	char b; 
    }; 
    cout<<sizeof(test);  //是4,最大的是int,4字節
    
  • 對類對象進行sizeof時,大體和struct類似,但要注意虛函數、繼承等關係,虛函數需要有指針指向虛函數表故會增加一個指針的空間,繼承時會繼承父類的空間

    class test
    {
    	~test() { }
    };   //sizeof(test)=1 
    class test2
    {
    	virtual ~test2() {}  
    };  //sizeof(test2)=8 因爲是虛函數,所以需要創建一個指針指向虛函數表,佔據內存
    
局部變量和全局變量
  • 作用域:全局變量在函數外聲明,作用域是整個源程序。局部變量在函數內或循環內聲明或作爲函數形參,作用域是當前函數或者循環等。
  • 內存存儲:全局變量存儲在全局數據區,局部變量存儲在棧區
  • 生命期:全局變量存在於整個執行過程,在程序啓動時創建,直至程序結束才銷燬。局部變量隨着函數或者循環的結束就銷燬了。
  • 如果在函數內部定義了與全局變量相同的局部變量,則在該函數內部優先使用局部變量
  • 局部靜態對象:static 局部靜態變量在程序的執行路徑第一次經過時初始化,直至程序終止才被銷燬
一個面試題
  • 有極大量的數據,10億個int數據,如何去重?

    思路:可以建立hash映射,只需要一個bit存儲即可,如果一個數字已經出現則之後遇到就去重,這樣至多需要MAX_INT個bit存儲,內存是可存的。

    或者使用布隆過濾器

參數傳遞的一些細節
  • 如果形參是引用類型,則將綁定到對應的實參;否則將會將實參的值拷貝後賦給形參
  • 傳值參數的修改將不影響具體的實參值,而傳引用參數的修改將直接修改具體的實參;通過傳指針形參,也可以間接地修改對象的值
  • 如果參數是比較大的類對象或者容器對象,通常傳引用可以避免低效地拷貝(甚至一些類型不支持拷貝)
  • 如果函數無須改變形參的值,則最好將其聲明爲傳引用,且常量引用
  • 數組作爲形參時,實際上傳遞的是指向數組首元素的指針。以數組作爲形參時要確保不要越界
  • 不要返回局部變量的引用或指針! 因爲在函數完成後其存儲空間將被釋放掉,會導致訪問的內存非法
  • 可以在形參列表中賦值設置默認實參,設置默認實參的參數應該放在右邊
重載overload與重寫override
  • 重載:同一作用域內函數名字相同,形參列表不同。不允許兩個函數除了返回類型外其他所有要素都相同

    不能根據返回值判斷是否重載;重載後,編譯器會根據參數選擇最優的調用,要避免二義性

  • 重寫:子類重新定義父類中有相同名稱和參數的虛函數(virtual)。在繼承關係之間。C++利用虛函數實現多態

  • 重寫函數必須具有相同的類型、名稱、參數列表

內聯函數 inline
  • 將函數聲明爲內聯函數將提高函數的執行效率,把關鍵詞inline放在函數定義前面即可。
  • 內聯函數執行時會將它在每個調用位置展開,避免了函數調用的開銷
  • 關鍵字 inline 必須與函數定義體放在一起才能使函數成爲內聯,僅將 inline 放在函數聲明前面不起任何作用
  • 不用盲目使用內聯,通常函數較爲簡短且簡單時才使用內聯
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章