C++面試題(二)

什麼是多態?

答:多態是指相同的操作或函數、過程可作用於多種類型的對象上並獲得不同的結果。不同的對象,收到同一消息可以產生不同的結果,這種現象稱爲多態。

 

解釋C++中靜態函數和靜態變量?

答:

(1)類靜態數據成員在編譯時創建並初始化:在該類的任何對象建立之前就存在,不屬於任何對象,而非靜態類成員變量則是屬於對象所有的。類靜態數據成員只有一個拷貝,爲所有此類的對象所共享。

(2)類靜態成員函數屬於整個類,不屬於某個對象,由該類所有對象共享。

1、static 成員變量實現了同類對象間信息共享。

2、static 成員類外存儲,求類大小,並不包含在內。

3、static 成員是命名空間屬於類的全局變量,存儲在 data 區的rw段。

4、static 成員只能類外初始化。

5、可以通過類名訪問(無對象生成時亦可),也可以通過對象訪問。

1、靜態成員函數的意義,不在於信息共享,數據溝通,而在於管理靜態數據成員,完成對靜態數據成員的封裝。

2、靜態成員函數只能訪問靜態數據成員。原因:非靜態成員函數,在調用時 this指針時被當作參數傳進。而靜態成員函數屬於類,而不屬於對象,沒有 this 指針。

 

不能做switch()的參數類型是:

答 、switch的參數不能爲實型。

 

 如何引用一個已經定義過的全局變量?

答 、可以用引用頭文件的方式,也可以用extern關鍵字,如果用引用頭文件方式來引用某個在頭文件中聲明的全局變理,假定你將那個變寫錯了,那麼在編譯期間會報錯,如果你用extern方式引用時,假定你犯了同樣的錯誤,那麼在編譯期間不會報錯,而在連接期間報錯

 

對於一個頻繁使用的短小函數,在C語言中應用什麼實現,在C++中應用什麼實現?

答 、c用宏定義,c++用inline

 

當一個類A 中沒有生命任何成員變量與成員函數,這時sizeof(A)的值是多少,請解釋一下編譯器爲什麼沒有讓它爲零。

答案:爲1。舉個反例,如果是零的話,聲明一個class A[10]對象數組,而每一個對象佔用的空間是零,這時就沒辦法區分A[0],A[1]…了。

 

STL庫用過嗎?常見的STL容器有哪些?算法用過哪幾個?

答:STL包括兩部分內容:容器和算法。(重要的還有融合這二者的迭代器)

容器,即存放數據的地方。比如array等。

在STL中,容器分爲兩類:序列式容器和關聯式容器。

序列式容器,其中的元素不一定有序,但都可以被排序。如:vector、list、deque、stack、queue、heap、priority_queue、slist;

關聯式容器,內部結構基本上是一顆平衡二叉樹。所謂關聯,指每個元素都有一個鍵值和一個實值,元素按照一定的規則存放。如:RB-tree、set、map、multiset、multimap、hashtable、hash_set、hash_map、hash_multiset、hash_multimap。

下面各選取一個作爲說明。

vector:它是一個動態分配存儲空間的容器。區別於c++中的array,array分配的空間是靜態的,分配之後不能被改變,而vector會自動重分配(擴展)空間。

set:其內部元素會根據元素的鍵值自動被排序。區別於map,它的鍵值就是實值,而map可以同時擁有不同的鍵值和實值。

算法,如排序,複製……以及個容器特定的算法。這點不用過多介紹,主要看下面迭代器的內容。

迭代器是STL的精髓,我們這樣描述它:迭代器提供了一種方法,使它能夠按照順序訪問某個容器所含的各個元素,但無需暴露該容器的內部結構。它將容器和算法分開,好讓這二者獨立設計。

 

數據結構會嗎?項目開發過程中主要用到那些?

答:數據結構中主要會用到數組,鏈表,樹(較少),也會用到棧和隊列的思想。

 

類的static變量在什麼時候初始化?函數的static變量在什麼時候初始化?

答:類的靜態成員變量在類實例化之前就已經存在了,並且分配了內存。函數的static變量在執行此函數時進行初始化。

 

堆和棧的區別?堆和棧的生命週期?

答:

一、堆棧空間分配區別:

1、棧(操作系統):由操作系統自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧;

2、堆(操作系統): 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收,分配方式倒是類似於鏈表。

二、堆棧緩存方式區別:

1、棧使用的是一級緩存, 他們通常都是被調用時處於存儲空間中,調用完畢立即釋放;

2、堆是存放在二級緩存中,生命週期由虛擬機的垃圾回收算法來決定(並不是一旦成爲孤兒對象就能被回收)。所以調用這些對象的速度要相對來得低一些。

三、堆棧數據結構區別:

堆(數據結構):堆可以被看成是一棵樹,如:堆排序;

棧(數據結構):一種先進後出的數據結構。

 

什麼是內存泄漏?面對內存泄漏和指針越界,你有哪些方法?你通常採用哪些方法來避免和減少這類錯誤?

答:用動態存儲分配函數動態開闢的空間,在使用完畢後未釋放,結果導致一直佔據該內存單元即爲內存泄露。

使用的時候要記得指針的長度。

malloc的時候得確定在那裏free.

對指針賦值的時候應該注意被賦值指針需要不需要釋放.

動態分配內存的指針最好不要再次賦值.

 

常用的排序算法有哪些?簡單描述幾個排序算法的優缺點?

答:選擇、冒泡、快速、**、希爾、歸併、堆排等。

1.快排:是冒泡排序的一種改進。

優點:快,數據移動少

缺點:穩定性不足

2.歸併:分治法排序,穩定的排序算法,一般用於對總體無序,但局部有序的數列。

優點:效率高O(n),穩定

缺點:比較佔用內存

 

用C++設計一個不能被繼承的類

         首先想到的是在C++ 中,子類的構造函數會自動調用父類的構造函數。同樣,子類的析構函數也會自動調用父類的析構函數。要想一個類不能被繼承,我們只要把它的構造函數和析構函數都定義爲私有函數。那麼當一個類試圖從它那繼承的時候,必然會由於試圖調用構造函數、析構函數而導致編譯錯誤。可是這個類的構造函數和析構函數都是私有函數了,我們怎樣才能得到該類的實例呢?這難不倒我們,我們可以通過定義靜態來創建和釋放類的實例。基於這個思路,我們可以寫出如下的代碼:

class FinalClass1

{

public:

      static FinalClass1* GetInstance()

      {

            return new FinalClass1;

      }

 

      static void DeleteInstance( FinalClass1* pInstance)

      {

            delete pInstance;

            pInstance =0;

      }

 

private:

      FinalClass1(){}

      ~FinalClass1(){}

};

 

有n個臺階,如果一次只能上1個或2個臺階,求一共有多少種上法

/*

思路:

如果只有一級臺階,n=1,很明顯只有一種跳法

如果有兩級臺階,n=2,則有兩種跳法,一種是跳兩下1級,一種是直接跳兩級

那麼我們來看看如果有n層臺階,可以怎麼跳:

n層臺階可以是這麼夠成的

1.第n層臺階是從第n-1層跳1級上來的

2.第n層臺階是從第n-2層直接跳2級上來的

所以可以得到n層的跳法總數是F(n)=F(n-1)+F(n-2)

*/ 

#include "stdafx.h" 

#include<iostream> 

using namespacestd; 

int Solve(intn) 

            if(n==1) 

        return 1; 

            if(n==2) 

                return 2; 

    return Solve(n-1)+Solve(n-2); 

 

int _tmain(intargc, _TCHAR* argv[]) 

            int num=Solve(4); 

            cout<<num<<endl; 

            system("pause"); 

            return 0; 

 

 

給定一個字符串,裏面會有連續重複出現的字符,比如aabbbcdde,要求把連續重複的字符只保留一個,比如上面的字符串處理之後就變成了abcde。

voidWipeOutRepeatStr(char* str) 

{    

    char *p,*q; 

    p=q=str; 

    while(*q!='\0') 

    { 

        q++; 

        cout<<*p; 

        while(*p==*q) 

        { 

            q++; 

        } 

        p=q; 

    } 

}

 

寫一個函數找出一個整數數組中,第二大的數

const intMINNUMBER = -32767 ; 

int find_sec_max(int data[] , int count) 

         int maxnumber = data[0] ;  

         int sec_max = MINNUMBER ; 

         for ( int i = 1 ; i < count ;i++) 

         { 

                   if ( data[i] > maxnumber) 

                   { 

                            sec_max = maxnumber; 

                            maxnumber = data[i]; 

                   } 

                   else 

                   { 

                            if ( data[i] >sec_max ) 

                            sec_max = data[i]; 

                   } 

         } 

         return sec_max ; 

 

 

分別寫出BOOL,int,float,指針類型的變量a 與“零”的比較語句。

答案:

BOOL : if ( !a ) or if(a)

int : if ( a == 0)

float : const EXPRESSION EXP = 0.000001

if ( a < EXP && a >-EXP)

pointer : if ( a != NULL) or if(a == NULL)

 

將“引用”作爲函數返回值類型的格式、好處和需要遵守的規則

格式:類型標識符 &函數名(形參列表及類型說明){ //函數體 }

好處:在內存中不產生被返回值的副本;(注意:正是因爲這點原因,所以返回一個局部變量的引用是不可取的。因爲隨着該局部變量生存期的結束,相應的引用也會失效,產生runtime error! 

注意事項:

(1)不能返回局部變量的引用。這條可以參照Effective C++[1]的Item 31。主要原因是局部變量會在函數返回後被銷燬,因此被返回的引用就成爲了"無所指"的引用,程序會進入未知狀態。

(2)不能返回函數內部new分配的內存的引用。這條可以參照Effective C++[1]的Item 31。雖然不存在局部變量的被動銷燬問題,可對於這種情況(返回函數內部new分配內存的引用),又面臨其它尷尬局面。例如,被函數返回的引用只是作爲一個臨時變量出現,而沒有被賦予一個實際的變量,那麼這個引用所指向的空間(由new分配)就無法釋放,造成memory leak。

(3)可以返回類成員的引用,但最好是const。這條原則可以參照Effective C++[1]的Item 30。主要原因是當對象的屬性是與某種業務規則(business rule)相關聯的時候,其賦值常常與某些其它屬性或者對象的狀態有關,因此有必要將賦值操作封裝在一個業務規則當中。如果其它對象可以獲得該屬性的非常量引用(或指針),那麼對該屬性的單純賦值就會破壞業務規則的完整性。

(4)流操作符重載返回值申明爲“引用”的作用:

流操作符<<和>>,這兩個操作符常常希望被連續使用,例如:cout << "hello" << endl; 因此這兩個操作符的返回值應該是一個仍然支持這兩個操作符的流引用。可選的其它方案包括:返回一個流對象和返回一個流對象指針。但是對於返回一個流對象,程序必須重新(拷貝)構造一個新的流對象,也就是說,連續的兩個<<操作符實際上是針對不同對象的!這無法讓人接受。對於返回一個流指針則不能連續使用<<操作符。因此,返回一個流對象引用是惟一選擇。這個唯一選擇很關鍵,它說明了引用的重要性以及無可替代性,也許這就是C++語言中引入引用這個概念的原因吧。 

賦值操作符=。這個操作符象流操作符一樣,是可以連續使用的,例如:x = j = 10;或者(x=10)=100;賦值操作符的返回值必須是一個左值,以便可以被繼續賦值。因此引用成了這個操作符的惟一返回值選擇。

 

#include<iostream.h>

int &put(int n);

int vals[10];

int error=-1;

void main()

{

put(0)=10; //以put(0)函數值作爲左值,等價於vals[0]=10; 

put(9)=20; //以put(9)函數值作爲左值,等價於vals[9]=20; 

cout<<vals[0]; 

cout<<vals[9];

int &put(int n)

{

if (n>=0 && n<=9 ) return vals[n]; 

else { cout<<"subscript error"; return error; }

}

(5)在另外的一些操作符中,卻千萬不能返回引用:+-*/ 四則運算符。它們不能返回引用,Effective C++[1]的Item23詳細的討論了這個問題。主要原因是這四個操作符沒有side effect,因此,它們必須構造一個對象作爲返回值,可選的方案包括:返回一個對象、返回一個局部變量的引用,返回一個new分配的對象的引用、返回一個靜態對象引用。根據前面提到的引用作爲返回值的三個規則,第2、3兩個方案都被否決了。靜態對象的引用又因爲((a+b) == (c+d))會永遠爲true而導致錯誤。所以可選的只剩下返回一個對象了。

 

TCP和UDP通信的差別?什麼是IOCP?

答:

1.TCP面向連接, UDP面向無連接的

2.TCP有保障的,UDP傳輸無保障的

3.TCP是效率低的,UDP效率高的

4.TCP是基於流的,UDP基於數據報文

5.TCP傳輸重要數據,UDP傳輸不重要的數據

IOCP全稱I/O Completion Port,中文譯爲I/O完成端口。

IOCP是一個異步I/O的API,它可以高效地將I/O事件通知給應用程序。

與使用select()或是其它異步方法不同的是,一個套接字[socket]與一個完成端口關聯了起來,然後就可繼續進行正常的Winsock操作了。然而,當一個事件發生的時候,此完成端口就將被操作系統加入一個隊列中。然後應用程序可以對核心層進行查詢以得到此完成端口。

 

同步IO和異步IO的區別?

答:

A. 同步

所謂同步,就是在發出一個功能調用時,在沒有得到結果之前,該調用就不返回。

按照這個定義,其實絕大多數函數都是同步調用(例如sin isdigit等)。

但是一般而言,我們在說同步、異步的時候,特指那些需要其他部件協作或者需要一定時間完成的任務。

最常見的例子就是SendMessage。

該函數發送一個消息給某個窗口,在對方處理完消息之前,這個函數不返回。

當對方處理完畢以後,該函數才把消息處理函數所返回的值返回給調用者。

B. 異步

異步的概念和同步相對。

當一個異步過程調用發出後,調用者不會立刻得到結果。

實際處理這個調用的部件是在調用發出後,通過狀態、通知來通知調用者,或通過回調函數處理這個調用。

 

SendMessage和PostMessage有什麼區別

答案:SendMessage是阻塞的,等消息被處理後,代碼才能走到SendMessage的下一行。PostMessage是非阻塞的,不管消息是否已被處理,代碼馬上走到PostMessage的下一行。

 

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