c++面試個人知識點記錄

目錄

1)C/C++基礎

C和C++區別:

什麼是面向對象:

虛函數:

C++內存管理:

New和malloc的區別:

多線程編程:

進程間通信:

內存映射:

雙緩衝:

Socket:

函數參數入棧:

MFC:

2)操作系統

3)計算機網絡

4)圖像採集TWAIN協議


1)C/C++基礎

C和C++區別:

首先就是C和C++區別,這裏轉載一篇面試總結 https://blog.csdn.net/bitboss/article/details/62884694

C語言是面向過程的編程,它最重要的特點是函數,通過main函數來調用各個子函數。程序運行的順序都是程序員事先決定好的。

C++是面向對象的編程,類是它的主要特點,在程序執行過程中,先由主main函數進入,定義一些類,根據需要執行類的成員函數,過程的概念被淡化了(實際上過程還是有的,就是主函數的哪些語句),以類驅動程序運行,類就是對象,所以我們稱之爲面向對象程序設計。面向對象在分析和解決問題的時候,將涉及到的數據和數據的操作封裝在類中,通過類可以創建對象,以事件或消息來驅動對象執行處理。

C語言和C++的最大區別在於它們解決問題的思想方法不一樣。C語言主要用於嵌入式領域,驅動開發等與硬件直接打交道的領域, C++可以用於應用層開發,用戶界面開發等於操作系統打交道的領域。

C++既繼承了C強大的底層操作特性,又被賦予了教科書式的面向對象機制。它特性繁多,有其他面嚮對象語言鮮見的多繼承,有耐人尋味的對值傳遞與引用傳遞入木三分的區分以及const關鍵字,等等。C++就像是一把瑞士軍刀,或者像是一個工具箱,它爲你提供儘可能多的工具,多到讓不熟悉它的人無所適從,讓懂得如何使用它的人如魚得水。C++的種種特性使得它非常適合用來編寫底層數據結構,算法,庫等,是系統軟件開發以及數學模型構建等的強大武器庫,被譽爲工業級編程語言。

C++對C的“增強”,表現在以下幾個方面:

類型檢查更爲嚴格。增加了面向對象的機制。增加了泛型編程的機制(Template)。增加了異常處理。增加了運算符重載。增加了標準模板庫(STL)。增加了命名空間,避免全局命名衝突。

什麼是面向對象:

 面向對象是一種思想,是基於面向過程而言的,就是說面向對象是將功能等通過對象來實現,將功能封裝進對象之中,讓對象去實現具體的細節;這種思想是將數據作爲第一位,而方法或者說是算法作爲其次,這是對數據一種優化,操作起來更加的方便,簡化了過程。

面向對象有三大特徵:封裝性、繼承性、多態性

封裝性:指的是隱藏了對象的屬性和實現細節,僅對外提供公共的訪問方式,這樣就隔離了具體的變化,便於使用,提高了複用性和安全性

繼承性:就是兩種事物間存在着一定的所屬關係,那麼繼承的類就可以從被繼承的類中獲得一些屬性和方法;這就提高了代碼的複用性。繼承是作爲多態的前提的。

多態性父類或接口的引用指向了子類對象,這就提高了程序的擴展性,也就是說只要實現或繼承了同一個接口或類,那麼就可以使用父類中相應的方法,提高程序擴展性,但是多態有一點不好之處在於:父類引用不能訪問子類中的成員。多態是通過虛函數實現的。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針

class A
{
public:
    A(){}
    virtual void foo()
    {
        cout<<"This is A."<<endl;
    }
};
 
class B: public A
{
public:
    B(){}
    void foo()
    {
        cout<<"This is B."<<endl;
    }
};
 
int main(int argc, char *argv[])
{
    A *a = new B();
    a->foo();
    if(a != NULL)
    delete a;
    return 0;
}

上面的列子會顯示This is B.如果把virtual去掉,將顯示:This is A.前面的多態通過使用虛函數virtual void foo()來實現。

虛函數:

在某基類中聲明爲 virtual 並在一個或多個派生類中被重新定義的成員函數,用法格式爲:

virtual 函數返回類型 函數名(參數表) {函數體};

實現多態性,通過指向派生類的基類指針或引用,訪問派生類中同名覆蓋成員函數,多態性是將接口與實現進行分離。我們只需在把基類的成員函數設爲virtual,其派生類的相應的函數也會自動變爲虛函數。

C++內存管理:

分爲5個儲存區:https://www.cnblogs.com/mrlsx/p/5411874.html

棧(Stack):局部變量,函數參數等存儲在該區,由編譯器自動分配和釋放.棧屬於計算機系統的數據結構,進棧出棧有相應的計算機指令支持,而且分配專門的寄存器存儲棧的地址,效率分高,內存空間是連續的,但棧的內存空間有限。

堆(Heap):需要程序員手動分配和釋放(new,delete),屬於動態分配方式。內存空間幾乎沒有限制,內存空間不連續,因此會產生內存碎片。操作系統有一個記錄空間內存的鏈表,當收到內存申請時遍歷鏈表,找到第一個空間大於申請空間的堆節點,將該節點分配給程序,並將該節點從鏈表中刪除。一般,系統會在該內存空間的首地址處記錄本次分配的內存大小,用於delete釋放該內存空間。

全局/靜態存儲區:全局變量,靜態變量分配到該區,到程序結束時自動釋放,包括DATA段(全局初始化區)與BBS段(全局未初始化段)。其中,初始化的全局變量和靜態變量存放在DATA段,未初始化的全局變量和靜態變量存放在BBS段。BBS段特點:在程序執行前BBS段自動清零,所以未初始化的全局變量和靜態變量在程序執行前已經成爲0.

文字常量區:存放常量,而且不允許修改。程序結束後由系統釋放。

程序代碼區:存放程序的二進制代碼

New和malloc的區別:

https://blog.csdn.net/happyxieqiang/article/details/50775847

(1) new 返回指定類型的指針,並且可以自動計算所需要大小。而 malloc 則必須要由我們計算字節數,並且在返回後強行轉換爲實際類型的指針。 
例:

//new
int *p;   
p = new int; //返回類型爲int* 類型(整數型指針),分配大小爲 sizeof(int); 
int* parr;   
parr = new int [100]; //返回類型爲 int* 類型(整數型指針),分配大小爲sizeof(int) * 100;

//malloc
int* p;   
p = (int *) malloc (sizeof(int)*128);//分配128個(可根據實際需要替換該數值)整型存儲單元,並將這128個連續的整型存儲單元的首地址存儲到指針變量p中  
double *pd=(double *) malloc (sizeof(double)*12);//分配12個double型存儲單元,並將首地址存儲到指針變量pd中

(2) malloc 只管分配內存,並不能對所得的內存進行初始化,所以得到的一片新內存中,其值將是隨機的。new創建的對象可以用初始化變量的方式初始化。

除了分配及最後釋放的方法不一樣以外,通過malloc或new得到指針,在其它操作上保持一致。

多線程編程:

其實C++語言本身並沒有提供多線程機制,但Windows系統爲我們提供了相關API,我們可以使用它們來進行多線程編程。

基本概念:

  1. 進程。程序是計算機指令的集合。它以文件的形式存儲在磁盤上。進程通常被定義爲一個正在運轉的程序的實例,是一個程序在其自身地址空間中的一次執行活動。是併發執行的程序在執行過程中分配和管理資源的基本單位,是一個動態概念,競爭計算機系統資源的基本單位。
  2. 線程是進程的一個執行單元,是進程內科調度實體。比進程更小的獨立運行的基本單位。線程也被稱爲輕量級進程。一個程序至少一個進程,一個進程至少一個線程。

進程是資源(CPU、內存等)分配的基本單位,它是程序執行時的一個實例。程序運行時系統就會創建一個進程,併爲它分配資源,然後把該進程放入進程就緒隊列,進程調度器選中它的時候就會爲它分配CPU時間,程序開始真正運行。線程是程序執行時的最小單位,它是進程的一個執行流,是CPU調度和分派的基本單位,一個進程可以由很多個線程組成,線程間共享進程的所有資源,每個線程有自己的堆棧和局部變量。線程由CPU獨立調度執行,在多CPU環境下就允許多個線程同時運行。同樣多線程也可以實現併發操作,每個請求分配一個線程來處理。

在Windows的多線程編程中,創建線程的函數主要有CreateThread和_beginthread(及_beginthreadex)。

      3. 互斥對象(mutex)。屬於內核對象。能夠確保線程擁有對單個資源的互斥訪問權。創建互斥對象需要使用CreateMutex函數。

使用互斥對象還會用到的兩個函數是 WaitForSingleObject和ReleaseMutex,函數聲明可以百度或查閱MSDN,這兩個函數的功能分別是申請互斥對象的擁有權和釋放互斥對象的擁有權。線程必須主動申請共享對象的使用權纔有可能得到該所有權。

如果一個線程擁有了一個互斥對象後,當該線程運行完成後就要釋放該互斥對象,不然其他的線程得不到互斥對象則無法運行。用ReleaseMutex(HWND)它的具體作用是每調用它一次將互斥對象的計數器減一,直到減到零爲止,此時釋放互斥對象,並將互斥對象中的線程id置零。它的使用條件是,互斥對象在哪個線程中被創建,就在哪個線程裏面釋放。因爲調用的時候會檢查當前線程的id是不是
與互斥對象中保存的id一致,若一致,則此次操作有效,不一致,則無效。

通過互斥對象也可以保證一個應用程序只有一個實例運行。

      4.線程同步。除了之前的互斥對象實現線程同步,還有事件對象和關鍵代碼段兩種方法。

進程間通信:

進程間通信(Interprocess Communication, IPC),經典的IPC:管道、FIFO、消息隊列、信號量以及共享存儲和套接字。

MFC上說的是剪貼板、匿名管道、命名管道、郵槽

內存映射:

WIN32的內存映射文件確實允許我們分配一個裝得下現實中可能存在的足夠大的文件的內存。利用內存映射文件您可以認爲操作系統已經爲您把文件全部裝入了內存,然後您只要移動文件指針進行讀寫即可了。這樣您甚至不需要調用那些分配、釋放內存塊和文件輸入/輸出的API函數,另外您可以把這用作不同的進程之間共享數據的一種辦法。運用內存映射文件實際上沒有涉及實際的文件操作,它更象爲每個進程保留一個看得見的內存空間。至於把內存映射文件當成進程間共享數據的辦法來用,則要加倍小心,因爲您不得不處理數據的同步問題,否則您的應用程序也許很可能得到過時或錯誤的數據甚至崩潰。

內存映射文件與虛擬內存有些類似,通過內存映射文件可以保留一個地址空間的區域,同時將物理存儲器提交給此區域,只是內存文件映射的物理存儲器來自一個已經存在於磁盤上的文件,而非系統的頁文件,而且在對該文件進行操作之前必須首先對文件進行映射,就如同將整個文件從磁盤加載到內存。由此可以看出,使用內存映射文件處理存儲於磁盤上的文件時,將不必再對文件執行I/O操作,這意味着在對文件進行處理時將不必再爲文件申請並分配緩存,所有的文件緩存操作均由系統直接管理,由於取消了將文件數據加載到內存、數據從內存到文件的回寫以及釋放內存塊等步驟,使得內存映射文件在處理大數據量的文件時能起到相當重要的作用。另外,實際工程中的系統往往需要在多個進程之間共享數據,如果數據量小,處理方法是靈活多變的,如果共享數據容量巨大,那麼就需要藉助於內存映射文件來進行。實際上,內存映射文件正是解決本地多個進程間數據共享的最有效方法。
內存映射文件並不是簡單的文件I/O操作,實際用到了Windows的核心編程技術--內存管理。

首先要通過CreateFile()函數來創建或打開一個文件內核對象,這個對象標識了磁盤上將要用作內存映射文件的文件。在用CreateFile()將文件映像在物理存儲器的位置通告給操作系統後,只指定了映像文件的路徑,映像的長度還沒有指定。爲了指定文件映射對象需要多大的物理存儲空間還需要通過CreateFileMapping()函數來創建一個文件映射內核對象以告訴系統文件的尺寸以及訪問文件的方式。在創建了文件映射對象後,還必須爲文件數據保留一個地址空間區域,並把文件數據作爲映射到該區域的物理存儲器進行提交。由MapViewOfFile()函數負責通過系統的管理而將文件映射對象的全部或部分映射到進程地址空間。此時,對內存映射文件的使用和處理同通常加載到內存中的文件數據的處理方式基本一樣,在完成了對內存映射文件的使用時,還要通過一系列的操作完成對其的清除和使用過資源的釋放。這部分相對比較簡單,可以通過UnmapViewOfFile()完成從進程的地址空間撤消文件數據的映像、通過CloseHandle()關閉前面創建的文件映射對象和文件對象。

雙緩衝:

  雙緩衝甚至是多緩衝,在許多情況下都很有用。一般需要使用雙緩衝區的地方都是由於“生產者”和“消費者”供需不一致所造成的。這樣的情況在很多地方後可能會發生,使用多緩衝可以很好的解決。我舉幾個常見的例子:

    例 1. 在網絡傳輸過程中數據的接收,有時可能數據來的太快來不及接收導致數據丟失。這是由於“發送者”和“接收者”速度不一致所致,在他們之間安排一個或多個緩衝區來存放來不及接收的數據,讓速度較慢的“接收者”可以慢慢地取完數據不至於丟失。

     例2. 再如,計算機中的三級緩存結構:外存(硬盤)、內存、高速緩存(介於CPU和內存之間,可能由多級)。從左到右他們的存儲容量不斷減小,但速度不斷提升,當然價格也是越來越貴。作爲“生產者”的 CPU 處理速度很快,而內存存取速度相對CPU較慢,如果直接在內存中存取數據,他們的速度不一致會導致 CPU  能力下降。因此在他們之間又增加的高速緩存來作爲緩衝區平衡二者速度上的差異。

     例3. 在圖形圖像顯示過程中,計算機從顯示緩衝區取數據然後顯示,很多圖形的操作都很複雜需要大量的計算,很難訪問一次顯示緩衝區就能寫入待顯示的完整圖形數據,通常需要多次訪問顯示緩衝區,每次訪問時寫入最新計算的圖形數據。而這樣造成的後果是一個需要複雜計算的圖形,你看到的效果可能是一部分一部分地顯示出來的,造成很大的閃爍不連貫。而使用雙緩衝,可以使你先將計算的中間結果存放在另一個緩衝區中,但全部的計算結束,該緩衝區已經存儲了完整的圖形之後,再將該緩衝區的圖形數據一次性複製到顯示緩衝區。

     例1 中使用雙緩衝是爲了防止數據丟失,例2 中使用雙緩衝是爲了提高 CPU 的處理效率,而例3使用雙緩衝是爲了防止顯示圖形時的閃爍延遲等不良體驗。

Socket:

socket即套接字,用於描述地址和端口,是一個通信鏈的句柄。應用程序通過socket向網絡發出請求或者回應。

sockets(套接字)編程有三種,流式套接字(SOCK_STREAM),數據報套接字(SOCK_DGRAM),原始套接字(SOCK_RAW);前兩種較常用。基於TCP的socket編程是採用的流式套接字。

編程步驟

(1)服務端

1、加載套接字庫,創建套接字(WSAStartup()/socket());

2、綁定套接字到一個IP地址和一個端口上(bind());

3、將套接字設置爲監聽模式等待連接請求(listen());

4、請求到來後,接受連接請求,返回一個新的對應於此次連接的套接字(accept());

5、用返回的套接字和客戶端進行通信(send()/recv());

6、返回,等待另一個連接請求;

7、關閉套接字,關閉加載的套接字庫(closesocket()/WSACleanup());

(2)客戶端

1、加載套接字庫,創建套接字(WSAStartup()/socket());

2、向服務器發出連接請求(connect());

3、和服務器進行通信(send()/recv());

4、關閉套接字,關閉加載的套接字庫(closesocket()/WSACleanup());

 

函數參數入棧:

https://blog.csdn.net/u012329294/article/details/12447405 

從右到左的參數方式,原因是參數都是用壓棧方式實現的,使用從右到左的傳參方式,棧頂看到的就是左邊輸入的首參數,因此,無論怎樣的變長,都可以通過指針偏移的方式找到值。而從左到右的話,棧頂看到的是最後一個參數,並不知道這個參數長度,那麼就無法通過指針偏移的方式找到首參數。

這裏https://blog.csdn.net/weichaohnu/article/details/8798581


void foo(int x, int y, int z)
{
        printf("x = %d at [%X]\n", x, &x);
        printf("y = %d at [%X]\n", y, &y);
        printf("z = %d at [%X]\n", z, &z);
}
int main(int argc, char *argv[])
{
        foo(100, 200, 300);
        return 0;
}

運行結果是:

x = 100 at [...60]

y = 200 at [...64]

z = 300 at [...68]

這是由於,C程序棧的內存生長方式是往低地址內存生長,這也說明爲什麼局部變量無法申請太大內存,因爲棧內容有限。此外,這個例子說明,函數參數的入棧的順序是從右往左的!

MFC:

2)操作系統

 

3)計算機網絡

 

4)圖像採集TWAIN協議

 

 

 

 

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