1.、為什麼構造函數不能聲明為虛函數?
解析:因為虛函數採用的是虛調用的方法,虛調用是指允許在只知道部分信息的情況下
的工作機制,特別允許我們調用一個只知道接口而不知道其對象的準確類型的函數。
但是如果我們要調用構造函數創建對象時,必須要知道對象的準確類型,
因此構造函數不能為虛函數。
2.C++中哪些函數不能被聲明為虛函數?
解析:普通函數(非成員函數),構造函數,內聯成員函數、靜態成員函數、友元函數。
(1)虛函數用於基類和派生類,普通函數所以不能
(2)構造函數不能是因為虛函數採用的是虛調用的方法,
(3)內聯成員函數的實質是在調用的地方直接將代碼擴展開
(4)繼承時,靜態成員函數不能被繼承的,它只屬於一個類,因為也不存在動態聯編
(5)友元函數不是類的成員函數,因此也不能被繼承
3.類的靜態成員和非靜態成員有何區別?
答:類的靜態成員每個類只有一個,靜態成員為所有類的實例對象共享,靜態成員有靜態成員變量和靜態成員函數,靜態成員變量使用前必須初始化,靜態成員變量可以被靜態成員函數和非靜態成員函數訪問,而靜態成員函數只能訪問靜態成員變量,因為靜態成員函數屬於類,其沒有this指針。非靜態成員每個對象都有一個。
4 重載(overload)和重寫(overried,有的書也叫做“覆蓋”)的區別?
重載:是指允許存在多個同名函數,而這些函數的參數表不同(或許參數個數不同,或許參數類型不同,或許兩者都不同)。 重寫:是指子類重新定義復類虛函數的方法。 從實現原理上來說:重載:編譯器根據函數不同的參數表,對同名函數的名稱做修飾,然後這些同名函數就成了不同的函數(至少對於編譯器來說是這樣的)。如,有兩個同名函數:function func(p:integer):integer;和function func(p:string):integer;。那麼編譯器做過修飾後的函數名稱可能是這樣的:int_func、str_func.對於這兩個函數的調用,在編譯器間就已經確定了,是靜態的。也就是說,它們的地址在編譯期就綁定了(早綁定),因此,重載和多態無關! 重寫:和多態真正相關。當子類重新定義了父類的虛函數後,父類指針根據賦給它的不同的子類指針,動態的調用屬於子類的該函數,這樣的函數調用在編譯期間是無法確定的(調用的子類的虛函數的地址無法給出)。因此,這樣的函數地址是在運行期綁定的(晚綁定)
5 簡述成員函數、全局函數和友元函數的差別。
成員函數只能由該類所實例化的對象來進行調用。[靜態成員除外]
全局函數可以在任意位置進行調用。
友元函數可以讓本類和友元類對象調用。
用new分配的內存用delete刪除用new[]分配的內存用delete[]刪除delete[]會調用數組元素的析構函數。內部數據類型沒有析構函數,所以問題不大。如果你在用delete時沒用括號,delete就會認為指向的是單個對象,否則,它就會認為指向的是一個數組。
6.繼承的優缺點。
類繼承是在編譯時刻靜態定義的,且可直接使用,類繼承可以較方便地改變父類的實現。但是類繼承也有一些不足之處。首先,因為繼承在編譯時刻就定義了,所以無法在運行時刻改變從父類繼承的實現。更糟的是,父類通常至少定義了子類的部分行為,父類的任何改變都可能影響子類的行為。如果繼承下來的實現不適合解決新的問題,則父類必須重寫或被其他更適合的類替換。這種依賴關係限制了靈活性並最終限制了複用性。 (待補充)
7.C++有哪些性質(面向對象特點)
封裝,繼承和多態。
在面向對象程序設計語言中,封裝是利用可重用成分構造軟件系統的特性,它不僅支持系統的可重用性,而且還有利於提高系統的可擴充性;消息傳遞可以實現發送一個通用的消息而調用不同的方法;封裝是實現信息隱蔽的一種技術,其目的是使類的定義和實現分離。
8. 在什麼時候需要使用“常引用”?
如果既要利用引用提高程序的效率,又要保護傳遞給函數的數據不在函數中被改變,就 應使用常引用。常引用聲明方式:const 類型標識符 &引用名=目標變量名;
int a
const int &ra=a;
ra=1; //錯誤
a=1; //正確
例2 string foo( );
void bar(string & s);
那麼下面的表達式將是非法的:
bar(foo( ));
bar("hello world");
原因在於foo( )和"hello world"串都會產生一個臨時對象,而在C++中,這些臨時對象都是const類型的。因此上面的表達式就是試圖將一個const類型的對象轉換為非const類型,這是非法的。引用型參數應該在能被定義為const的情況下,儘量定義為const
9.ASSERT()是幹什麼用的
答:ASSERT()是一個調試程序時經常使用的宏,在程序運行時它計算括號內的表達式,如果表達式為FALSE (0),
程序將報告錯誤,並終止執行。如果表達式不為0,則繼續執行後面的語句。這個宏通常原來判斷程序中是否出現了明顯非法的數據,如果出現了終止程序以免導致嚴重後果,同時也便於查找錯誤。例如,變量n在程序中不應該為0,如果為0可能導致錯誤,
10. 實現多態的方法?
解析: ① 一個基類的引用可以指向它的派生類實例
② 一個基類的指針可以指向它的派生類實例
11. 面向對象的三個基本特徵,並簡單敘述之?
① 封裝:將客觀事物抽象成類,每個類對自身的數據和方法實行訪問控制(private, protected,public)
② 繼承:廣義的繼承有三種實現形式:
實現繼承(指使用基類的屬性和方法而無需額外編碼的能力)
可視繼承(子窗體使用父窗體的外觀和實現代碼)
接口繼承(僅使用屬性和方法,實現滯後到子類實現)。
前兩種(類繼承)和後一種(對象組合=>接口繼承以及純虛函數)構成了功能複用的兩種方式。
③ 多態:是將父對象設置成為和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。
補充問題: 多態的作用?
主要是兩個:
1. 隱藏實現細節,使得代碼能夠模塊化;擴展代碼模塊,實現代碼重用;
2. 接口重用:為了類在繼承和派生的時候,保證使用家族中任一類的實例的某一屬性時的正確調用。
12重載(overload)和重寫(overried,有的書也叫做“覆蓋”)的區別?
① 重載:是指允許存在多個同名函數,而這些函數的參數表不同(或許參數個數不同,或許參數類型不同,或許兩者都不同)。
② 重寫:是指子類重新定義父類虛函數的方法。
從實現原理上來說:
① 重載:編譯器根據函數不同的參數表,對同名函數的名稱做修飾,然後這些同名函數就成了不同的函數(至少對於編譯器來說是這樣的)。對於這類函數的調用,在編譯期間就已經確定了,是靜態的。也就是說,它們的地址在編譯期就綁定了(早綁定),因此,重載和多態無關!
② 重寫:和多態真正相關。當子類重新定義了父類的虛函數後,父類指針根據賦給它的不同的子類指針,動態的調用屬於子類的該函數,這樣的函數調用在編譯期間是無法確定的(調用的子類的虛函數的地址無法給出)。因此,這樣的函數地址是在運行期綁定的(晚綁定)
13多態的作用?
主要是兩個:
① 隱藏實現細節,使得代碼能夠模塊化;擴展代碼模塊,實現代碼重用;
② 接口重用:為了類在繼承和派生的時候,保證使用家族中任一類的實例的某一屬性時的正確調用。
14、C++中的空類,默認產生的類成員函數:
class
{
public:
Empty(); // 缺省構造函數
Empty(const Empty&); // 拷貝構造函數
~Empty(); // 析構函數
Empty& operator=(const Empty&); // 賦值運算符
Empty* operator&(); // 取值運算符
const Empty* operator&() const; // 取值運算符const
};
15.進程間通信的方式有?
進程間通信的方式有 :共享內存, 管道(有名管道/無名管道),Socket ,消息隊列 ,信號,信號量,內存映射等。
16死鎖的四個必要條件?
互斥,請求保持,不可剝奪,環路。
17、類的靜態成員和非靜態成員有什麼區別?
類的靜態成員每個類只有一個,即是屬於本類的;類的非靜態成員每個對象都有一份。
18什麼是淺拷貝?什麼是深拷貝?
淺拷貝是指源對象與拷貝對象共用一份實體,僅僅是引用的變量不同(名稱不同)。對其中任何一個對象的改動都會影響另外一個對象。
深拷貝是指源對象與拷貝對象互相獨立,其中任何一個對象的改動都不會對另外一個對象造成影響。
一般來說,淺拷貝就是複製那個對象的指針。深拷貝就是複製了那個對象。
19、Windows編程線程同步的幾種方式?(重要)
原子鎖、臨界區(段)、事件、互斥(體)、信號量、可等候定時器
20什麼是“引用”?申明和使用“引用”要注意哪些問題?
答:引用就是某個目標變量的“別名”(alias),對引用的操作與對變量直接操作效果完全相同。申明一個引用的時候,切記要對其進行初始化。引用聲明完畢後,相當於目標變量名有兩個名稱,即該目標原名稱和引用名,不能再把該引用名作為其他變量名的別名。聲明一個引用,不是新定義了一個變量,它只表示該引用名是目標變量名的一個別名,它本身不是一種數據類型,因此引用本身不佔存儲單元,系統也不給引用分配存儲單元。不能建立數組的引用。
21. “引用”與指針的區別是什麼?
指針通過某個指針變量指向一個對象後,對它所指向的變量間接操作。程序中使用指針,程序的可讀性差;而引用本身就是目標變量的別名,對引用的操作就是對目標變量的操作。此外,就是上面提到的對函數傳ref和pointer的區別。
22. 關聯、聚合(Aggregation)以及組合(Composition)的區別?
涉及到UML中的一些概念:關聯是表示兩個類的一般性聯繫,比如“學生”和“老師”就是一種關聯關係;聚合表示has-a的關係,是一種相對鬆散的關係,聚合類不需要對被聚合類負責,如下圖所示,用空的菱形表示聚合關係:
從實現的角度講,聚合可以表示為:
class A {...} class B { A* a; .....}
而組合表示contains-a的關係,關聯性強於聚合:組合類與被組合類有相同的生命週期,組合類要對被組合類負責,採用實心的菱形表示組合關係:
實現的形式是:
class A{...} class B{ A a; ...}
23.面向對象的三個基本特徵,並簡單敘述之?
1. 封裝:將客觀事物抽象成類,每個類對自身的數據和方法實行protection(private, protected,public)
2. 繼承:廣義的繼承有三種實現形式:實現繼承(指使用基類的屬性和方法而無需額外編碼的能力)、可視繼承(子窗體使用父窗體的外觀和實現代碼)、接口繼承(僅使用屬性和方法,實現滯後到子類實現)。前兩種(類繼承)和後一種(對象組合=>接口繼承以及純虛函數)構成了功能複用的兩種方式。
3. 多態:是將父對象設置成為和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。
24 多態的作用?
主要是兩個:1. 隱藏實現細節,使得代碼能夠模塊化;擴展代碼模塊,實現代碼重用;2. 接口重用:為了類在繼承和派生的時候,保證使用家族中任一類的實例的某一屬性時的正確調用。
25 在C++ 程序中調用被 C編譯器編譯後的函數,為什麼要加 extern “C”聲明?
//extern是C/C++語言中表明函數和全局變量作用範圍的關鍵字,該關鍵字告訴編譯器,其聲明的函數和變量可以在本模塊或其它模塊中使用
// extern “C是連接申明,編譯時告訴編譯器以下代碼用C風格的方式編譯和連接,其目的是實現C++與C及其它語言的混合編程。
26.“引用”與指針的區別是什麼?
指針通過某個指針變量指向一個對象後,對它所指向的變量間接操作。程序中使用指針,程序的可讀性差;而引用本身就是目標變量的別名,對引用的操作就是對目標變量的操作。此外,就是上面提到的對函數傳ref和pointer的區別。
23.New delete 與malloc free 的聯繫與區別?
答案:都是在堆(heap)上進行動態的內存操作。用malloc函數需要指定內存分配的字節數並且不能初始化對象,new 會自動調用對象的構造函數。delete 會調用對象的destructor,而free 不會調用對象的destructor.
24 成員函數被重載的特徵:
(1)相同的範圍(在同一個類中);
(2)函數名字相同;
(3)參數不同;
(4)virtual 關鍵字可有可無。
25覆蓋是指派生類函數覆蓋基類函數,特徵是:
(1)不同的範圍(分別位於派生類與基類);
(2)函數名字相同;
(3)參數相同;
(4)基類函數必須有virtual 關鍵字。
26如果用VC開發程序,常見這麼幾個錯誤,C2001,c2005,c2011,這些錯誤的原因是什麼。
在學習VC++的過程中,遇到的LNK2001錯誤的錯誤消息主要為:
unresolved external symbol “symbol”(不確定的外部“符號”)。
如果連接程序不能在所有的庫和目標文件內找到所引用的函數、變量或標籤,將產生此錯誤消息。
一般來說,發生錯誤的原因有兩個:一是所引用的函數、變量不存在、拼寫不正確或者使用錯誤;
其次可能使用了不同版本的連接庫。
編程中經常能遇到LNK2005錯誤——重複定義錯誤,其實LNK2005錯誤並不是一個很難解決的錯誤.
27介紹一下STL,詳細說明STL如何實現vector。
STL (標準模版庫,Standard Template Library)它由容器算法迭代器組成。
STL有以下的一些優點:
可以方便容易地實現搜索數據或對數據排序等一系列的算法;
調試程序時更加安全和方便;
即使是人們用STL在UNIX平臺下寫的代碼你也可以很容易地理解(因為STL是跨平臺的)。
vector實質上就是一個動態數組,會根據數據的增加,動態的增加數組空間。
28介紹一下模板和容器。如何實現?(也許會讓你當場舉例實現)
模板可以說比較古老了,但是當前的泛型編程實質上就是模板編程。
它體現了一種通用和泛化的思想。
STL有7種主要容器:vector,list,deque,map,multimap,set,multiset.
29 :簡述多態實現的原理
編譯器發現一個類中有虛函數,便會立即為此類生成虛函數表 vtable。虛函數表的各表項為指向對應虛函數的指針。編譯器還會在此類中隱含插入一個指針vptr(對vc編譯器來說,它插在類的第一個位置上)指向虛函數表。調用此類的構造函數時,在類的構造函數中,編譯器會隱含執行vptr與vtable的關聯代碼,將vptr指向對應的vtable,將類與此類的vtable聯繫了起來。另外在調用類的構造函數時,指向基礎類的指針此時已經變成指向具體的類的this指針,這樣依靠此this指針即可得到正確的vtable,。如此才能真正與函數體進行連接,這就是動態聯編,實現多態的基本原理。
30:談談你對面向對象的認識
解析:面向對象可以理解成對待每一個問題,都是首先要確定這個問題由幾個部分組成,而每一個部分其實就是一個對象。然後再分別設計這些對象,最後得到整個程序。傳統的程序設計多是基於功能的思想來進行考慮和設計的,而面向對象的程序設計則是基於對象的角度來考慮問題。這樣做能夠使得程序更加的簡潔清晰。
說明:編程中接觸最多的“面向對象編程技術”僅僅是面向對象技術中的一個組成部分。發揮面向對象技術的優勢是一個綜合的技術問題,不僅需要面向對象的分析,設計和編程技術,而且需要藉助必要的建模和開發工具。
31 C++中為什麼用模板類。
解析:
1) 可用來創建動態增長和減小的數據結構
2) 它是類型無關的,因此具有很高的可複用性。
3) 它在編譯時而不是運行時檢查數據類型,保證了類型安全
4) 它是平臺無關的,可移植性
5) 可用於基本數據類型
32 函數模板與類模板有什麼區別?
答:函數模板的實例化是由編譯程序在處理函數調用時自動完成的,而類模板的實例化
必須由程序員在程序中顯式地指定。
33 .winsock建立連接的主要實現步驟? (非常重要,必問)
答:服務器端:socker()建立套接字,綁定(bind)並監聽(listen),用accept()
等待客戶端連接。
客戶端:socker()建立套接字,連接(connect)服務器,連接上後使用send()和recv(
),在套接字上寫讀數據,直至數據交換完畢,closesocket()關閉套接字。
服務器端:accept()發現有客戶端連接,建立一個新的套接字,自身重新開始等待連
接。該新產生的套接字使用send()和recv()寫讀數據,直至數據交換完畢,closesock
et()關閉套接字。
34 進程和線程的差別。
答:線程是指進程內的一個執行單元,也是進程內的可調度實體.與進程的區別:
(1)調度:線程作為調度和分配的基本單位,進程作為擁有資源的基本單位
(2)併發性:不僅進程之間可以併發執行,同一個進程的多個線程之間也可併發執行
(3)擁有資源:進程是擁有資源的一個獨立單位,線程不擁有系統資源,但可以訪問隸屬於進程的資源.
(4)系統開銷:在創建或撤消進程時,由於系統都要為之分配和回收資源,導致系統的開銷明顯大於創建或撤消線程時的開銷。
35. C++是不是類型安全的?
答案:不是。兩個不同類型的指針之間可以強制轉換(用reinterpret cast)。C#是類型安全的。
- 如果用VC開發程序,常見這麼幾個錯誤,C2001,c2005,c2011,這些錯誤的原因是什麼。
37宏與內聯函數的區別
解析:內聯函數和宏都是在程序出現的地方展開,內聯函數不是通過函數調用實現的,是在調用該函數的程序處將它展開(在編譯期間完成的);宏同樣是;
不同的是:內聯函數可以在編譯期間完成諸如類型檢測,語句是否正確等編譯功能;宏就不具有這樣的功能,而且宏展開的時間和內聯函數也是不同的(在運行期間展開)
38 win32中消息循環Windows程序的入口是哪裡?寫出Windows消息機制的流程
解析:Windows程序的入口是WinMain()函數。
Windows應用程序消息處理機制:
A. 操作系統接收應用程序的窗口消息,將消息投遞到該應用程序的消息隊列中
B. 應用程序在消息循環中調用GetMessage函數從消息隊列中取出一條一條的消息,取出消息後,應用程序可以對消息進行一些預處理。
C. 應用程序調用DispatchMessage,將消息回傳給操作系統。
D. 系統利用WNDCLASS結構體的lpfnWndProc成員保存的窗口過程函數的指針調用窗口過程,對消息進行處理。
39:談談你對編程規範的理解或認識
編程規範可總結為:程序的可行性,可讀性、可移植性以及可測試性。
說明:這是編程規範的總綱目,面試者不一定要去背誦上面給出的那幾個例子,應該去理解這幾個例子說明的問題,想一想,自己如何解決可行性、可讀性、可移植性以及可測試性這幾個問題,結合以上幾個例子和自己平時的編程習慣來回答這個問題。
40流操作符重載為什麼返回引用
在程序中,流操作符>>和<
注意:除了在賦值操作符和流操作符之外的其他的一些操作符中,如+、-、*、/等卻千萬不能返回引用。因為這四個操作符的對象都是右值,因此,它們必須構造一個對象作為返回值。
41 是不是一個父類寫了一個virtual 函數,如果子類覆蓋它的函數不加virtual ,也能實現多態?
virtual修飾符會被隱形繼承的。private 也被集成,只事派生類沒有訪問權限而已。virtual可加可不加。子類的空間裡有父類的所有變量(static除外)。同一個函數只存在一個實體(inline除外)。子類覆蓋它的函數不加virtual ,也能實現多態。在子類的空間裡,有父類的私有變量。私有變量不能直接訪問。
42.C和C++有什麼不同?
從機制上:c是面向過程的(但c也可以編寫面向對象的程序);c++是面向對象的,提供了類。但是,c++編寫面向對象的程序比c容易
從適用的方向:c適合要求代碼體積小的,效率高的場合,如嵌入式;c++適合更上層的,複雜的; llinux核心大部分是c寫的,因為它是系統軟件,效率要求極高。
從名稱上也可以看出,c++比c多了+,說明c++是c的超集;那為什麼不叫c+而叫c++呢,是因為c++比
c來說擴充的東西太多了,所以就在c後面放上兩個+;於是就成了c++
C語言是結構化編程語言,C++是面向對象編程語言。
C++側重於對象而不是過程,側重於類的設計而不是邏輯的設計。
43預處理器標識#error的目的是什麼?
如果你不知道答案,請看參考文獻1。這問題對區分一個正常的夥計和一個書呆子是很有用的。只有書呆子才會讀C語言課本的附錄去找出象這種
問題的答案。當然如果你不是在找一個書呆子,那麼應試者最好希望自己不要知道答案。
44.請簡述以下兩個for循環的優缺點
// 第一個
for (i=0; i++;){if (condition)DoSomething();else DoOtherthing();}
// 第二個
if (condition){for (i=0; i++;)DoSomething();}
else{for (i=0; i++;)DoOtherthing();}
閱讀更多 編程學習 的文章