C++筆試題總結(二)

16. 關聯、聚合(Aggregation)以及組合(Composition)的區別?

涉及到UML(建模語言)中的一些概念:關聯是表示兩個類的一般性聯繫,比如“學生”和“老師”就是一種關聯關係;聚合表示has-a的關係,是一種相對鬆散的關係,聚合類不需要對被聚合類負責,如下圖所示,用空的菱形表示聚合關係:

從實現的角度講,聚合可以表示爲:

class A {...} class B { A* a; .....}

而組合表示contains-a的關係,關聯性強於聚合:組合類與被組合類有相同的生命週期,組合類要對被組合類負責,採用實心的菱形表示組合關係:

實現的形式是:

class A{...} class B{ A a; ...}

參考文章:http://blog.csdn.net/wfwd/archive/2006/05/30/763753.aspx

http://blog.csdn.net/wfwd/archive/2006/05/30/763760.aspx


17.面向對象的三個基本特徵,並簡單敘述之?

1. 封裝:將客觀事物抽象成類,每個類對自身的數據和方法實行protection(private, protected,public)

2. 繼承:廣義的繼承有三種實現形式:實現繼承(指使用基類的屬性和方法而無需額外編碼的能力)、可視繼承(子窗體使用父窗體的外觀和實現代碼)、接口繼承(僅使用屬性和方法,實現滯後到子類實現)。前兩種(類繼承)和後一種(對象組合=>接口繼承以及純虛函數)構成了功能複用的兩種方式。

3. 多態:是將父對象設置成爲和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。


18. 重載(overload)和重寫(overried,有的書也叫做“覆蓋”)的區別?

常考的題目。從定義上來說:

重載:是指允許存在多個同名函數,而這些函數的參數表不同(或許參數個數不同,或許參數類型不同,或許兩者都不同)。

重寫:是指子類重新定義復類虛函數的方法

從實現原理上來說:

重載:編譯器根據函數不同的參數表,對同名函數的名稱做修飾,然後這些同名函數就成了不同的函數(至少對於編譯器來說是這樣的)。如,有兩個同名函數:function func(p:integer):integer;function func(p:string):integer;。那麼編譯器做過修飾後的函數名稱可能是這樣的:int_funcstr_func。對於這兩個函數的調用,在編譯器間就已經確定了,是靜態的。也就是說,它們的地址在編譯期就綁定了(早綁定),因此,重載和多態無關!

重寫:和多態真正相關。當子類重新定義了父類的虛函數後,父類指針根據賦給它的不同的子類指針,動態的調用屬於子類的該函數,這樣的函數調用在編譯期間是無法確定的(調用的子類的虛函數的地址無法給出)。因此,這樣的函數地址是在運行期綁定的(晚綁定)。


19. 多態的作用?

主要是兩個:1. 隱藏實現細節,使得代碼能夠模塊化;擴展代碼模塊,實現代碼重用;2. 接口重用:爲了類在繼承和派生的時候,保證使用家族中任一類的實例的某一屬性時的正確調用。


20. AdoAdo.net的相同與不同?

除了“能夠讓應用程序處理存儲於DBMS 中的數據“這一基本相似點外,兩者沒有太多共同之處。但是Ado使用OLE DB 接口並基於微軟的COM 技術,而ADO.NET 擁有自己的ADO.NET 接口並且基於微軟的.NET 體系架構。衆所周知.NET 體系不同於COM 體系,ADO.NET 接口也就完全不同於ADOOLE DB 接口,這也就是說ADO.NET ADO是兩種數據訪問方式。ADO.net 提供對XML 的支持。


21. New delete malloc free 的聯繫與區別?

答案:都是在堆(heap)上進行動態的內存操作。用malloc函數需要指定內存分配的字節數並且不能初始化對象,new 會自動調用對象的構造函數。delete 會調用對象的destructor,而free 不會調用對象的destructor.


22. #define DOUBLE(x) x+x i = 5*DOUBLE(5); 是多少?

答案:30


23. 有哪幾種情況只能用intialization list 而不能用assignment?

答案:當類中含有constreference 成員變量;基類的構造函數都需要初始化表。


24. C++是不是類型安全的?

答案:不是。兩個不同類型的指針之間可以強制轉換(用reinterpret cast)C#是類型安全的。


25. main 函數執行以前,還會執行什麼代碼?

答案:全局對象的構造函數會在main 函數之前執行。


26. 描述內存分配方式以及它們的區別?

1) 從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量,static 變量。

2) 在棧上創建。在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集。

3) 從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc new 申請任意多少的內存,程序員自己負責在何時用free delete 釋放內存。動態內存的生存期由程序員決定,使用非常靈活,但問題也最多。


27.struct 和 class 的區別

答案:struct 的成員默認是公有的,而類的成員默認是私有的struct 和 class 在其他方面是功能相當的。

從感情上講,大多數的開發者感到類和結構有很大的差別。感覺上結構僅僅象一堆缺乏封裝和功能的開放的內存位,而類就象活的並且可靠的社會成員,它有智能服務,有牢固的封裝屏障和一個良好定義的接口。既然大多數人都這麼認爲,那麼只有在你的類有很少的方法並且有公有數據(這種事情在良好設計的系統中是存在的!)時,你也許應該使用 struct 關鍵字,否則,你應該使用 class 關鍵字。


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

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


29. 8086 彙編下,邏輯地址和物理地址是怎樣轉換的?(Intel

答案:通用寄存器給出的地址,是段內偏移地址,相應段寄存器地址*10H+通用寄存器內地址,就得到了真正要訪問的地址。


30. 比較C++中的4種類型轉換方式?

請參考:http://blog.csdn.net/wfwd/archive/2006/05/30/763785.aspx,重點是static_cast, dynamic_castreinterpret_cast的區別和應用。

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

答案:

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)

 

32.請說出const#define 相比,有何優點?

答案:1) const 常量有數據類型,而宏常量沒有數據類型。編譯器可以對前者進行類型安全檢查。而對後者只進行字符替換,沒有類型安全檢查並且在字符替換可能會產生意料不到的錯誤。

2) 有些集成化的調試工具可以對const 常量進行調試,但是不能對宏常量進行調試。


33.簡述數組與指針的區別?

數組要麼在靜態存儲區被創建(如全局數組),要麼在棧上被創建。指針可以隨時指向任意類型的內存塊。

(1)修改內容上的差別

char a[] = hello;

a[0] = X;

char *p = world; // 注意指向常量字符串

p[0] = X; // 編譯器不能發現該錯誤,運行時錯誤

(2) 用運算符sizeof 可以計算出數組的容量(字節數)。sizeof(p),p 爲指針得到的是一個指針變量的字節數,而不是所指的內存容量。C++/C 語言沒有辦法知道指針所指的內存容量,除非在申請內存時記住它。注意當數組作爲函數的參數進行傳遞時,該數組自動退化爲同類型的指針。

char a[] = "hello world";

char *p = a;

cout<< sizeof(a) << endl; // 12 字節

cout<< sizeof(p) << endl; // 4 字節

計算數組和指針的內存容量

void Func(char a[100])

{

cout<< sizeof(a) << endl; // 4 字節而不是100 字節

}


34.類成員函數的重載、覆蓋和隱藏區別?

答案:

a.成員函數被重載的特徵:

1)相同的範圍(在同一個類中);

2)函數名字相同;

3)參數不同;

4virtual 關鍵字可有可無。

b.覆蓋是指派生類函數覆蓋基類函數,特徵是:

1)不同的範圍(分別位於派生類與基類);

2)函數名字相同;

3)參數相同;

4)基類函數必須有virtual 關鍵字。

c.“隱藏”是指派生類的函數屏蔽了與其同名的基類函數,規則如下:

1)如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)。

2)如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual 關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)


35. There are two int variables: a and b, dont use if? :switchor other judgement statements, find out the biggest one of the two numbers.

答案:( ( a + b ) + abs( a - b ) ) / 2


36. 如何打印出當前源文件的文件名以及源文件的當前行號?

答案:

cout << __FILE__ ;

cout<<__LINE__ ;

__FILE____LINE__是系統預定義宏,這種宏並不是在某個文件中定義的,而是由編譯器定義的。


37. main 主函數執行完畢後,是否可能會再執行一段代碼,給出說明?

答案:可以,可以用_onexit 註冊一個函數,它會在main 之後執行int fn1(void), fn2(void), fn3(void), fn4 (void);

void main( void )

{

String str("zhanglin");

_onexit( fn1 );

_onexit( fn2 );

_onexit( fn3 );

_onexit( fn4 );

printf( "This is executed first.\n" );

}

int fn1()

{

printf( "next.\n" );

return 0;

}

int fn2()

{

printf( "executed " );

return 0;

}

int fn3()

{

printf( "is " );

return 0;

}

int fn4()

{

printf( "This " );

return 0;

}

The _onexit function is passed the address of a function (func) to be called when the program terminates normally. Successive calls to _onexit create a register of functions that are executed in LIFO (last-in-first-out) order. The functions passed to _onexit cannot take parameters.


38. 如何判斷一段程序是由編譯程序還是由C++編譯程序編譯的?

答案:

#ifdef __cplusplus

cout<<"c++";

#else

cout<<"c";

#endif


39.文件中有一組整數,要求排序後輸出到另一個文件中

答案:

i nclude

i nclude

using namespace std;

 

void Order(vector& data) //bubble sort

{

int count = data.size() ;

int tag = false ; // 設置是否需要繼續冒泡的標誌位

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

{

for ( int j = 0 ; j < count - i - 1 ; j++)

{

if ( data[j] > data[j+1])

{

tag = true ;

int temp = data[j] ;

data[j] = data[j+1] ;

data[j+1] = temp ;

}

}

if ( !tag )

break ;

}

}

 

void main( void )

{

vectordata;

ifstream in("c:\\data.txt");

if ( !in)

{

cout<<"file error!";

exit(1);

}

int temp;

while (!in.eof())

{

in>>temp;

data.push_back(temp);

}

in.close(); //關閉輸入文件流

Order(data);

ofstream out("c:\\result.txt");

if ( !out)

{

cout<<"file error!";

exit(1);

}

for ( i = 0 ; i < data.size() ; i++)

out<out.close(); //關閉輸出文件流

}

 

40. 鏈表題:一個鏈表的結點結構

struct Node

{

int data ;

Node *next ;

};

typedef struct Node Node ;

 

(1)已知鏈表的頭結點head,寫一個函數把這個鏈表逆序 ( Intel)

Node * ReverseList(Node *head) //鏈表逆序

{

if ( head == NULL || head->next == NULL )    //考慮特殊情況!!!

return head;

Node *p1 = head ;

Node *p2 = p1->next ;

Node *p3 ;

p1->next = NULL ;

while ( p2 )

{

p3 = p2->next ;

p2->next = p1 ;

p1 = p2 ;    //P1指向P2所指!!

p2 = p3 ;

}

head = p1;

return head ;

}

 

 

(2)已知兩個鏈表head1 head2 各自有序,請把它們合併成一個鏈表依然有序。(保留所有結點,即便大小相同)

Node * Merge(Node *head1 , Node *head2)

{

if ( head1 == NULL)

return head2 ;

if ( head2 == NULL)

return head1 ;

Node *head = NULL ;

Node *p1 = NULL;

Node *p2 = NULL;

if ( head1->data < head2->data )

{

head = head1 ;

p1 = head1->next;

p2 = head2 ;

}

else

{

head = head2 ;

p2 = head2->next ;

p1 = head1 ;

}

Node *pcurrent = head ;

while ( p1 != NULL && p2 != NULL)

{

if ( p1->data <= p2->data )

{

pcurrent->next = p1 ;

pcurrent = p1 ;

p1 = p1->next ;

}

else

{

pcurrent->next = p2 ;

pcurrent = p2 ;

p2 = p2->next ;

}

}

if ( p1 != NULL )

pcurrent->next = p1 ;

if ( p2 != NULL )

pcurrent->next = p2 ;

return head ;

}

(3)已知兩個鏈表head1 head2 各自有序,請把它們合併成一個鏈表依然有序,這次要求用遞歸方法進行。 (Autodesk)

答案:

Node * MergeRecursive(Node *head1 , Node *head2)

{

if ( head1 == NULL )

return head2 ;

if ( head2 == NULL)

return head1 ;

Node *head = NULL ;

if ( head1->data < head2->data )

{

head = head1 ;

head->next = MergeRecursive(head1->next,head2); //每次把剩下的看做兩個新鏈表進行合併!!

}

else

{

head = head2 ;

head->next = MergeRecursive(head1,head2->next);

}

return head ;

}

41. 分析一下這段程序的輸出 (Autodesk)

class B

{

public:

B()

{

cout<<"default constructor"<}

~B()

{

cout<<"destructed"<}

B(int i):data(i)                          //B(int) works as a converter ( int -> instance of B)

{

cout<<"constructed by parameter " << data <}

private:

int data;

};

 

B Play( B b)

{

 return b ;

}

(1) results:

int main(int argc, char* argv[]) constructed by parameter 5

{ destructed B(5)形參析構

B t1 = Play(5); B t2 = Play(t1);   destructed t1形參析構

return 0;               destructed t2 注意順序!

} destructed t1

(2) results:

int main(int argc, char* argv[]) constructed by parameter 5

{ destructed B(5)形參析構

B t1 = Play(5); B t2 = Play(10);   constructed by parameter 10

return 0;               destructed B(10)形參析構

} destructed t2 注意順序!

destructed t1


42. 寫一個函數找出一個整數數組中,第二大的數 (microsoft

答案:

const int MINNUMBER = -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 ;

}


43. 寫一個在一個字符串(n)中尋找一個子串(m)第一個位置的函數。

KMP算法效率最好,時間複雜度是O(n+m)


44. 多重繼承的內存分配問題:

比如有class A : public class B, public class C {}

那麼A的內存結構大致是怎麼樣的?

這個是compiler-dependent不同的實現其細節可能不同。

如果不考慮有虛函數、虛繼承的話就相當簡單;否則的話,相當複雜。

可以參考《深入探索C++對象模型》,或者:

http://blog.csdn.net/wfwd/archive/2006/05/30/763797.aspx


45. 如何判斷一個單鏈表是有環的?(注意不能用標誌位,最多隻能用兩個額外指針)

struct node { char val; node* next;}

bool check(const node* head) {} //return false : 無環;true: 有環

一種On)的辦法就是(搞兩個指針,一個每次遞增一步,一個每次遞增兩步,如果有環的話兩者必然重合。從此次重合開始計數,到下次重合結束,即爲環的的長度):

bool check(const node* head)

{

if(head==NULL) return false;           //特殊情況

node *low=head, *fast=head->next;

while(fast!=NULL && fast->next!=NULL)  //如果有NULL則無環

{

low=low->next;      

fast=fast->next->next;

if(low==fast) return true;

}

return false;

}


46給定一個單鏈表,只給出頭指針h

1、如何判斷是否存在環?

2、如何知道環的長度?

3、如何找出環的連接點在哪裏?

4、帶環鏈表的長度是多少?

 

解法:

1、對於問題1,使用追趕的方法,設定兩個指針slowfast,從頭指針開始,每次分別前進1步、2步。如存在環,則兩者相遇;如不存在環,fast遇到NULL退出。

2、對於問題2,記錄下問題1的碰撞點pslowfast從該點開始,再次碰撞所走過的操作數就是環的長度s

3、問題3:有定理:碰撞點p到連接點的距離=頭指針到連接點的距離,因此,分別從碰撞點、頭指針開始走,相遇的那個點就是連接點。(證明在後面附註)

4、問題3中已經求出連接點距離頭指針的長度,加上問題2中求出的環的長度,二者之和就是帶環單鏈表的長度

 

 

其它:

http://www.doc88.com/p-77832826312.html

發佈了52 篇原創文章 · 獲贊 10 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章