1. 程式人生 > >(轉)看完了第二遍C++Primer,學習心得與問題

(轉)看完了第二遍C++Primer,學習心得與問題

學習C++ Primer時遇到的問題及解釋

chenm91

感覺:

l          囉嗦有時會掩蓋主題:這本書確實有些囉嗦,比如在講函式過載的時候,講了太長一大段(有兩節是打了*號的,看還是不看?),而在TC++PL中則精煉很多。這讓人有些很難接受,很多時候好像讓人有些找不到綱了。

l          舉例偏難有時也會掩蓋主題:這本書使用的例子“文字查詢系統”有些偏難。在講述C++的一些關鍵性概念的時候,應該將讀者的注意力都集中在這些概念上,可是通過這樣一個難懂的例子來講述,我覺得無疑是將問題複雜化了,尤其是在講述面向物件和STL相關概念時,我無法接受,儘管確實通過這個例子顯示了C++的NB之處。而在TC++PL中,不管講什麼概念,大都採用了一些簡單易懂的例子,讓人很舒服的一針見血的享受關鍵的知識點。

l          將STL分開講不太好:這本書將“容器類”放到基本語言篇,將“泛型演算法”放到基於過程的程式設計篇。儘管有他的道理,但是實際上STL是屬於泛型程式設計的,還是將STL獨立起來講述比較好。讀者也好對STL做一個選擇:學還是不學。TC++PL則將其獨立起來。

l          還是TC++PL好些:不管怎樣,這本書確實很全面地介紹C++的幾乎所有知識點,有淺有深。但是總的來看這本書有點碩士畢業論文的味道,太冗長,有湊頁數的味道。TC++PL則有期刊論文的味道。看完兩遍C++ Primer,學到不少東西,不過還是準備以TC++PL為主,再看一遍。

1、在VC的MFC程式中能否使用cout輸出?

答:不能。必須使用CDC來進行輸出。

2、C++中子類的建構函式是否會自動呼叫基類建構函式?

答:會。如果在子類的建構函式中不顯式指定呼叫基類建構函式,則將自動呼叫基類預設建構函式,所以基類一定要有預設建構函式。如果子類的建構函式除了呼叫基類建構函式以外什麼都不做,不能不寫,必須寫一個空函式。

3、子類物件的初始化過程是怎樣的?

答:先呼叫基類建構函式來初始化相關的基類子物件,然後再執行子類(派生類)的建構函式。

4、名字空間的使用有哪些方式?

答:大概有三種方式:

l          使用名字空間別名加上名稱限制修飾符

namespace LIB = IBM_Canada_Laboratory;

LIB::Array<int> ia(1024);

l          using指示符,使名字空間內的所有宣告可見

using namespace IBM_Canada_Laboratory;

l          using宣告,使名字空間中的單個宣告可見

using IBM_Canada_Laboratory::Matrix;

using指示符和using宣告的本質是不同的。

5、如何宣告一個物件已經在檔案外被定義?

答:使用extern來宣告。可以在多個地方多次宣告同一個物件,但是一個物件只能被定義一次,而且多個宣告必須明確無誤的指向這個定義實體。宣告不分配記憶體,定義分配記憶體。

6、類的預設建構函式會為類的資料成員初始化嗎?

答:不會。只會分配記憶體,不會初始化。必須在預設建構函式中顯式進行初始化。

7、下面的物件初始化是呼叫賦值操作符嗎?

BaseClass base1; BaseClass base2 = base1;

答:不是。是呼叫拷貝建構函式。

8、類中的常量資料成員應該在哪裡被初始化?

答:應該在類的成員初始化列表中初始化。

9、typedef引入了新的型別嗎?

答:沒有。它僅僅是用來為內建或使用者定義的資料型別引入助記符號。

10、空指標解引用會出錯嗎?

答:會。對指標解引用之前一定要先確定其不為零。

11、算術異常會丟擲實際異常嗎?

答:算術異常是指計算過程中出現不正確或未定義的值,如除以0或溢位。但是不會導致丟擲實際的異常。

12、size_t是什麼型別?

答:這是用typedef引入的型別助記符,代替unsigned int。

13、用sizeof可以獲得一個數組的長度,為什麼在傳遞陣列給函式時還要帶上長度引數?

答:用sizeof是可以獲取一個數組的長度,如int a[10]; int len = sizeof(a)/sizeof(int);。但是,在用陣列作為引數傳遞給函式時,陣列變為了指標,所以在函式內部不能獲取陣列長度。

14、算術轉換的指導原則是什麼?

答:有兩個通用原則:一、型別總是被提升為較寬的型別;二、所有含有小於整型的有序型別,在計算之前,其型別都會被轉換成整型。

15、都有哪些顯式強制轉換操作符?它們的功能是什麼?

答:有static_cast、const_cast、dynamic_cast和reinterpret_cast。static_cast可用來執行任何編譯器可隱式執行的任何型別轉換。const_cast用來將常量物件或指標轉換為非常量物件或指標。reinterpret_cast通常對於運算元的位模式執行一個比較低層次的重新解釋,相當於舊式的指標強制轉換。dynamic_cast支援在執行時刻識別由指標或引用指向的類物件。

16、對於類物件的定義為什麼最好在用之前才定義,而不要在一開始就定義?

答:對於內建型別,它的定義只是分配記憶體,不會做更多的處理。而對於類物件,它的定義除了分配記憶體外,還要呼叫建構函式。因此,如果在函式或語句塊一開始就定義類物件,必然會導致建構函式的呼叫,而這些呼叫的開銷可能是不必要的。這使得程式的效率下降。因此,最好是當真正要用的時候才去定義比較好。

17、容器型別的容量與長度的區別?

答:容量只與連續儲存的容器類相關,如vector、deque或string,而對於非連續儲存的容器類,如list,則沒有容量的概念。它是指在容器下一次需要增長自己之前能夠被加入到容器中的元素的總數,可呼叫capacity()操作來獲得。長度是指容器當前擁有元素的個數,可呼叫size()操作來獲得。容量的增長方式是與具體實現有關的。

18、迭代器的基本概念是什麼?

答:迭代器提供了一種一般化的方法,對順序或關聯容器型別中的每個元素進行連續訪問。

19、什麼時候函式應該採用引用引數?

答:一、希望改變實參的值,又不想用指標操作;二、希望返回更多的值;三、實參為大型類物件,按值傳遞效率低。

20、函式引數表中的省略號是什麼意思?

答:省略號告訴編譯器不用對這個函式的引數進行引數型別檢查了,引數可以有0個或多個。

21、extern的用法有哪些?

答:一、連結指示符,用來宣告函式由其他語言編寫,如extern “C”;二、宣告一個全域性變數,表示該變數在其他檔案中定義了。宣告函式時可省略掉。

22、函式名和函式名取地址都可以作為函式指標嗎?

答:可以。因此,函式指標和函式指標解引用都可以用來呼叫函式。

23、型別安全連結的含義是什麼?

答:在編譯階段,有一種機制可以把函式引數的型別和數目編碼在函式名中,該機制叫做型別安全連結。所以,一個函式並非僅僅通過函式名來識別的,這是函式過載的基礎。

24、靜態物件會被預設初始化嗎?

答:會。預設初始化為0。

25、用delete刪除動態記憶體分配的空間時,是否要先判斷指標為NULL?

答:不用,程式內部會自動判斷。但是最好在刪除之後將指標設為NULL。

26、using宣告和using指示符的本質有什麼不同?

答:using宣告相當於在宣告的語句處引入一個別名,該別名與宣告的名稱空間中的同名物件或函式一樣。using指示符相當於把名稱空間在其定義處就地剝開,並不引入新的別名。

27、函式過載時是否考慮const引數的影響?

答:對於const物件來說,函式過載不考慮,即認為帶有const和非const物件引數的函式是相同的函式宣告。而對於const指標或引用,則認為是不同的函式宣告。

28、extern “c”連結指示符能否將c函式引入到過載函式集合中?

答:能,但只能引入一個。即c語言的函式只能有一個屬於過載函式集合。

29、函式模板例項化的過程中,模板型別引數是如何被確定型別的?

答:是通過呼叫函式時的實參型別來推演模板的引數型別的,該過程稱為模板實參推演。

30、異常處理子句catch中宣告異常類物件有何用?

答:這個異常類物件可以捕獲異常發生時,由throw子句所產生異常類物件。如果改為異常類物件的引用,則還可進一步修改由throw子句丟擲的異常類物件,並重新丟擲。

31、如果函式丟擲了其異常規範之外的異常時會出現什麼情況?

答:系統呼叫C++標準庫定義的函式unexpected(),其將呼叫terminate()。

32、指定一個空的異常規範和不指定異常規範一樣嗎?

答:不一樣。空的異常規範表示該函式不能丟擲任何異常,而不指定異常規範表示該函式可以丟擲任何異常。

33、在程式設計中異常設計是否很困難?

答:是。異常是庫介面的一部分,什麼時候丟擲異常,什麼時候庫自己處理,在那些呼叫程式中處理丟擲的異常都是一個問題。

34、STL的主要組成部分包括哪些?

答:有容器類(包含資料)、演算法(對資料的操作)、迭代器(連線容器和演算法)以及函式物件(定義具體的操作方式)。

35、函式物件的一般用法如何?

答:一般將函式物件定義為一個類,然後需要過載該類的呼叫操作符。一般的泛型演算法都可以接受函式物件或函式指標。函式指標的用法缺點是不能支援函式內聯。

36、函式物件從哪裡來?

答:一、標準庫預定義的一組算術、關係和邏輯函式物件;二、定義自己的函式物件;三、一組預定義的函式介面卡,對函式物件進行特殊化或者擴充套件。

37、什麼是函式介面卡?

答:標準庫提供的一組特殊的類,用來特殊化或者擴充套件函式物件。它被分成下面兩類:繫結器(binder)和取反器(negator)。

38、排序演算法能用在list上嗎?

答:不能,也不能用在聯合容器set或map上。

39、類在定義之前能定義類物件嗎?

答:不能。但可以先宣告這個類,然後定義該類的指標或引用。

40、宣告const成員函式時是否要在定義函式時也加上const?

答:在宣告和定義函式的時候都必須加上const。

41、const成員函式有什麼要求?

答:要求在const函式當中不能修改類的資料成員。對於那些不修改資料成員的函式最好將其宣告為const函式。有一個例外,如果資料成員定義為mutable型別,則在const函式中也可以修改該資料成員。

42、靜態資料成員如何初始化?

答:應該在類體定義之外初始化,但不能在類的建構函式中初始化,因為靜態成員不屬於某個類物件,而是屬於類的。靜態資料成員可通過類的限定修飾符來訪問。

43、什麼是巢狀類?

答:在一個類A的類體中定義的另一個類B,稱為巢狀類(nested class)。

44、如何呼叫類的預設建構函式定義類物件?

答:不能這樣寫:ClassA obj(); 這樣會被認為聲明瞭一個函式,必須這樣寫:ClassA obj;。一般情況都必須為類提供預設建構函式。

45、應用在建構函式上的explicit有什麼作用?

答:一般建構函式如果只含有一個引數,則編譯器可以隱式的呼叫該建構函式,將引數型別轉換為類型別,但是這種隱式轉換在某些時候是存在隱患的,在建構函式前面加上explicit就是為了防止這種隱式呼叫發生。

46、一個類物件的容器類是如何初始化的?

答:先建立該類的臨時物件,並用預設建構函式初始化;然後將臨時物件通過拷貝建構函式拷貝到容器類當中的所有類物件上;然後刪除臨時物件。

47、類的成員類物件在建構函式中初始化時,放在成員初始化表中與放在建構函式體中有什麼區別?

答:實際上類的初始化分為兩個階段,第一個階段是初始化階段,它呼叫所有成員類的預設建構函式或者呼叫成員初始化表;第二個階段是計算階段,它呼叫函式體內的語句。所以如果將成員類物件放在函式體中初始化,實際上是先呼叫預設建構函式,然後執行賦值操作,效率大大降低。一般準則是所有的成員類物件都應該將其放在成員初始化表中。另外對於const成員和引用成員必須在成員初始化表中。

48、拷貝建構函式和拷貝賦值操作符是一回事嗎?

答:不是。但一般來說,兩者應該同時定義或者禁止。

49、前置和後置++和--操作符函式原型有什麼不同?

答:後置的操作符函式引數比前置的操作符函式多一個int引數,該引數在函式定義中並不會使用。

50、轉換函式與建構函式都可完成型別轉換,它們有什麼區別?

答:建構函式(帶有一個引數的)可完成其它型別向該類型別轉換操作,而轉換函式是從該類型別向指定的型別轉換的操作。轉換函式原型不能含有返回值和引數。它的定義形式如下:operator type() {}。

51、使用者定義的轉換包括什麼?

答:包括呼叫轉換函式或一個引數的建構函式。

52、標準C++支援為模板引數提供預設實參嗎?

答:支援,但VC++6.0不支援。

53、類模板的成員函式的例項化是不是隨著類模板的例項化進行的?

答:不是,成員函式的例項化是當函式被呼叫的時候才例項化的。

54、面向物件程式設計的基礎是什麼?

答:兩個基礎:多型(polymorphism)和動態繫結(dynamic binding)。多型指的是基類的指標或引用可以指向基類派生的任何子類,即一個基類指標或引用可能的實際型別是多種的。動態繫結指的是當用基類指標呼叫函式時,如果函式是虛擬函式(virtual function),則實際呼叫哪個函式是在執行時刻根據指標的實際型別來動態決定了。

55、在面向物件設計中是否一次性完成類結構設計?

答:一般很難,通常是反覆迭代,要求對不斷演化過程中的類層次結構進行新增和修改。

56、基類的一般用法如何?

答:定義基類的指標或引用(如果包含純虛擬函式,則不能定義抽象基類的物件),然後用它們來操縱任何從該基類派生來的子類物件。

57、面向物件基類的設計主要要考慮的問題是什麼?

答:確定哪些成員為protected;確定哪些函式為虛擬函式。

58、派生類的虛擬函式原型是否要指定virtual?

答:可要可不要。

59、派生類能否定義函式,以過載基類當中的函式?

答:可以。採用在派生類中使用using宣告可做到。

60、派生類的建構函式執行包括幾個步驟?

答:三個步驟:基類建構函式;成員類建構函式;派生類建構函式。

61、類的建構函式的訪問許可權只能是public嗎?

答:不是。可以是protected和private兩種。當建構函式為protected時,只希望在派生類中構造該類。當建構函式為private時,不希望任何地方來構造該類,如果有友元類,可以在友元類中構造該類。

62、派生類的建構函式要呼叫基類的預設建構函式,是否要顯式呼叫?

答:不用。會被隱式呼叫。

63、解構函式是否也可以做成動態繫結?

答:可以,只要宣告為虛擬函式。

64、虛擬函式在.cpp檔案中定義時是否要加virtual?

答:不能加。

65、如何靜態呼叫虛擬函式?

答:用類域限定符。

66、clone函式是幹什麼的?

答:是用來克隆一個類物件,並返回克隆後的物件指標。

67、dynamic_cast是幹什麼用的?

答:它是RTTI(執行時刻型別識別)的一部分。它支援在執行時刻查詢一個多型指標或引用所指向的物件的實際型別。

68、多繼承下的多個基類一般與派生類存在什麼關係?

答:一般每個基類代表派生類完整介面的一個方面。

69、private繼承意味著什麼?

答:由於基類的所有成員(public和protected成員)都變成子類的private成員,包括基類的公共介面也變成私有的了,因此,子類不能給外部直接提供基類的公共介面,而必須重新實現它們。這往往是由於子類僅僅希望得到基類的資料成員,但不希望繼承基類的介面。

70、什麼是RTTI?

答:執行時刻型別識別,可使得程式可以知道指向基類的指標或引用實際所指的物件的型別。它提供了兩個操作符:dynamic_cast,允許在執行時刻進行型別轉換,把基類指標轉換成派生類指標;typeid,指出指標或引用的實際派生型別。

71、typeid操作符如何使用?

答:typeid的引數如為類物件,則返回該類物件的實際型別。如為類指標,則返回該指標的定義型別。返回的值實際上是一個type_info類。

72、類成員函式的宣告和定義中都要指定異常規範嗎?

答:都要。