題目六: 1.改錯(cuò) #include #include class CBuffer { char m_pBuffer; int m_size; public: CBuffer() { m_pBuffer=NULL; } ~CBuffer() { Free(); } void Allocte(int size) (3) { m_size=size; m_pBuffer= new char[size]; } private: void Free() { if(m_pBuffer!=NULL) { delete m_pBuffer; m_pBuffer=NULL; } } public: void SaveString(const char pText) const { strcpy(m_pBuffer, pText); } char GetBuffer() const { return m_pBuffer; }}; void main (int argc, char argv[]) { CBuffer buffer1; buffer1.SaveString("Microsoft"); printf(buffer1.GetBuffer()); } 答:改正后 主要改正 SaveString 函數(shù) 將 void SaveString(const char pText) const { strcpy(m_pBuffer, pText); } 改為 void SaveString(const char pText) (1) { Allocte(strlen(pText)+1); (2) strcpy(m_pBuffer, pText); } 原因: (1) const 成員函數(shù)表示不會(huì)修改數(shù)據(jù)成員,而 SaveString 做不到,去掉 const 聲明 (2) m_pBuffer 指向 NULL,必須用 Allocte 分配空間才能賦值。 (3) 另外需要將 Allocte 成員函數(shù)聲明為私有成員函數(shù)更符合實(shí)際 2.下來(lái)程序想打印“Welcome MSR Asia”,改正錯(cuò)誤 #include #include char GetName (void) { //To return “MSR Asia” String char name[]="MSR Asia"; return name; } void main(int argc, char argv[]) { char name[32]; //Fill in zeros into name for(int i=0;i<=32;i++) { name[i]='\0';} //copy “Welcome” to name name="Welcome"; //Append a blank char name[8]=" "; //Append string to name strcat(name,GetName()); //print out printf(name); } 答:改正后為 #include #include char GetName (void) { //To return “MSR Asia” String //char name[]="MSR Asia"; (1) char name=(char )malloc(strlen("MSR Asia")+1); strcpy(name,"MSR Asia"); return name; } void main(int argc, char argv[]) { char name[32]; //Fill in zeros into name for(int i=0;i<=32;i++) { name[i]='\0'; } //copy “Welcome” to name //name="Welcome"; (2) strcat(name,"Welcome "); //Append a blank char // name[8]=' '; (3) //Append string to name char p=GetName(); (4) strcat(name,p); free (p); //print out printf(name); } 原因:(1)在函數(shù)內(nèi)部定義的變量在函數(shù)結(jié)束時(shí)就清空了,必須動(dòng)態(tài)分配內(nèi)存(2)字符串賦值語(yǔ)句錯(cuò)誤,應(yīng)該用 strcat (3)該語(yǔ)句無(wú)效,可去掉 (4)定義一個(gè)指針指向動(dòng)態(tài)分配的內(nèi)存,用完后需用 free 語(yǔ)句釋放 3.寫(xiě)出下面程序的輸出結(jié)果 #include class A { public: void FuncA() { printf("FuncA called\n"); } virtual void FuncB() { printf("FuncB called\n"); } }; class B: public A { public: void FuncA() { A::FuncA(); printf("FuncAB called\n"); } virtual void FuncB() { printf("FuncBB called\n"); } }; void main(void) { B b; A pa; pa=&b; A pa2=new A; b.FuncA(); (1) b.FuncB(); (2) pa->FuncA(); (3)pa->FuncB(); (4) pa2->FuncA(); (5) pa2->FuncB(); delete pa2; } 答: 1.b.FuncA(); 輸出 FuncA called FuncAB called 2.b.FuncB();輸出 FuncBB called 上兩者好理解,直接調(diào)用類(lèi) B 的相應(yīng)成員函數(shù) 3.pa->FuncA();輸出 FuncA called 調(diào)用類(lèi) A 的 FuncA() 4.pa->FuncB();輸出 FuncBB called 調(diào)用類(lèi) B 的 FuncB(),原因是 C++的動(dòng)態(tài)決議機(jī)制,當(dāng)基類(lèi)函數(shù)聲明為 virtual 時(shí),指向派生 類(lèi)對(duì)象的基類(lèi)指針來(lái)調(diào)用該函數(shù)會(huì)選擇派生類(lèi)的實(shí)現(xiàn),除非派生類(lèi)沒(méi)有才調(diào)用基類(lèi)的虛函數(shù)。還有一點(diǎn)注 意的是:指向基類(lèi)類(lèi)型的指針可以指向基類(lèi)對(duì)象也可以指向派生類(lèi)對(duì)象,如 pa=&b; 5. pa2->FuncA(); pa2->FuncB();輸出 FuncA called FuncB called 這也好理解,直接調(diào)用類(lèi) A 的相應(yīng)成員函數(shù) 4.In the main() function, after ModifyString(text) is called, what’s the value of ‘text’? #include #include int FindSubString(char pch) { int count=0; char p1=pch; while(p1!='\0') { if(p1==p1-1) { p1++; count++; } else { break; } }int count2=count; while(p1!='\0') { if(p1==p1+1) { p1++; count2--; } else { break; } } if(count2==0) return count; return 0; } void ModifyString(char pText) { char p1=pText; char p2=p1; while(p1!='\0') { int count=FindSubString(p1); if(count>0){ p2++=p1; sprintf(p2, "%I", count); while(p2!= '\0') { p2++; } p1+=count+count+1; } else{ p2++=p1++; }} void main(void) { char text[32]="XYBCDCBABABA"; ModifyString(text); printf(text); } 答:我不知道這個(gè)結(jié)構(gòu)混亂的程序到底想考察什么,只能將最后運(yùn)行結(jié)果寫(xiě)出來(lái)是 XYBCDCBAIBAAP1912 給定一單鏈表的表頭指針和指向其中一個(gè)節(jié)點(diǎn)的指針,要求以該指針為頭將原鏈表逆序排列,例如: N1->N2->N3->N4->N5->NULL pHEAD = N1,pSTART = N3,返回 N3->N2->N1->N5->N4->NULL N1->N2->N3->N4->N5->NULL pHEAD = N1,pSTART = N5,返回這個(gè) N5->N4->N3->N2->N1->NULL N1->N2->N3->N4->N5->NULL pHEAD = N1,pSTART = N1,返回這個(gè) N1->N5->N4->N3->N2->NULL 不允許額外分配存儲(chǔ)空間,不允許遞歸,可以使用臨時(shí)變量。 typedef struct node { int value; struct node next; } node; void ReverseList(node pHead, node pStart){ node t1, t2, it=pHead; t1 = it->next; while(t1 != 0){ t2 = t1->next; t1->next = (it == pStart?0:it); it = t1; t1 = t2; } (pHead)->next = it; pHead = pStart; }附加了測(cè)試代碼后的完整程序,抄了一部分 chaoslawful 的,呵呵 #include #include #include typedef struct node { int value;struct node next; } node; void ReverseList(node pHead, node pStart){ node t1, t2, it=pHead; t1 = it->next; while(t1 != 0){ t2 = t1->next; t1->next = (it == pStart?0:it); it = t1; t1 = t2; } (pHead)->next = it; pHead = pStart; } void PrintList(node pHead){ node it=pHead; while(it){ printf("%d ",it->value); it=it->next; } } void ConstructList(node pHead,...) { node q=0; va_list ap; int i; va_start(ap, pHead); while (1){ i = va_arg(ap,int); if(i<0) break; if(!q){ pHead=q=(node )malloc(sizeof(node)); q->value=i; q->next=0; } else { q->next=(node )malloc(sizeof(node)); q=q->next; q->value=i; q->next=0; }} } void SetStart(node it, node pStart, int i) { while(i-- > 0 && it->next != 0){ it = it->next; } pStart = it; } int main(){ node pHead=(node )malloc(sizeof(node )); node pStart=(node )malloc(sizeof(node )); ConstructList(&pHead,1,2,3,4,5,-1); PrintList(pHead); SetStart(pHead, &pStart, 2); ReverseList(&pHead, pStart); PrintList(pHead); return 1; } 題目七: 1.寫(xiě)出 a(b-cd)+e-f/g(h+ij-k)的逆波蘭表達(dá)式 2.面向?qū)ο笳Z(yǔ)言中 public,proteced,private 的區(qū)別 3.SAX 和 DOM 的區(qū)別以及各自優(yōu)缺點(diǎn) 4.進(jìn)程和線程區(qū)別 5.假設(shè)現(xiàn)有一個(gè)功能,用戶點(diǎn)擊一個(gè)按鈕后就會(huì)自動(dòng)發(fā)送一封郵件到用戶的郵箱。現(xiàn)在 用戶反映沒(méi)有受到郵件。你怎么去發(fā)現(xiàn)并解決問(wèn)題 6.用 Java 寫(xiě)一個(gè) Singleton 類(lèi) 7.2 個(gè)有序 List,請(qǐng)用 Java 寫(xiě)一個(gè)合并函數(shù)合并他們,返回一個(gè)有序 List public List Merge(List a,List b){ } 題目八: 1.求下面函數(shù)的返回值(微軟) int func(x) { int countx = 0; while(x) { countx ++; x = x&(x-1); } return countx; } 假定 x = 9999。 答案:8 思路:將 x 轉(zhuǎn)化為 2 進(jìn)制,看含有的 1 的個(gè)數(shù)。 2. 什么是“引用”?申明和使用“引用”要注意哪些問(wèn)題? 答:引用就是某個(gè)目標(biāo)變量的“別名”(alias),對(duì)應(yīng)用的操作與對(duì)變量直接操作效果完全相同。 申明一個(gè)引用的時(shí)候,切記要對(duì)其進(jìn)行初始化。引用聲明完畢后,相當(dāng)于目標(biāo)變量名有兩個(gè)名稱,即該目標(biāo)原名稱和引用名,不能再把該引用名作為其他變量名的別名。聲明一個(gè)引用,不是新定義了一個(gè)變量,它只表示該引用名是目標(biāo)變量名的一個(gè)別名,它本身不是一種數(shù)據(jù)類(lèi)型,因此引用本身不占存儲(chǔ)單元,系統(tǒng)也不給引用分配存儲(chǔ)單元。不能建立數(shù)組的引用。 3. 將“引用”作為函數(shù)參數(shù)有哪些特點(diǎn)? (1)傳遞引用給函數(shù)與傳遞指針的效果是一樣的。這時(shí),被調(diào)函數(shù)的形參就成為原來(lái)主調(diào)函數(shù)中的實(shí)參變量或?qū)ο蟮囊粋(gè)別名來(lái)使用,所以在被調(diào)函數(shù)中對(duì)形參變量的操作就是對(duì)其相應(yīng)的目標(biāo)對(duì)象(在主調(diào)函數(shù)中)的操作。 (2)使用引用傳遞函數(shù)的參數(shù),在內(nèi)存中并沒(méi)有產(chǎn)生實(shí)參的副本,它是直接對(duì)實(shí)參操作;而使用一般變量傳遞函數(shù)的參數(shù),當(dāng)發(fā)生函數(shù)調(diào)用時(shí),需要給形參分配存儲(chǔ)單元,形參變量是實(shí)參變量的副本;如果傳遞的是對(duì)象,還將調(diào)用拷貝構(gòu)造函數(shù)。因此,當(dāng)參數(shù)傳遞的數(shù)據(jù)較大時(shí),用引用比用一般變量傳遞參數(shù)的效率和所占空間都好。 (3)使用指針作為函數(shù)的參數(shù)雖然也能達(dá)到與使用引用的效果,但是,在被調(diào)函數(shù)中同樣要給形參分配存儲(chǔ)單元,且需要重復(fù)使用"指針變量名"的形式進(jìn)行運(yùn)算,這很容易產(chǎn)生錯(cuò)誤且程序的閱讀性較差;另一方面,在主調(diào)函數(shù)的調(diào)用點(diǎn)處,必須用變量的地址作為實(shí)參。而引用更容易使用,更清晰。 4. 在什么時(shí)候需要使用“常引用”? 如果既要利用引用提高程序的效率,又要保護(hù)傳遞給函數(shù)的數(shù)據(jù)不在函數(shù)中被改變,就應(yīng)使用常引用。常引用聲明方式:const 類(lèi)型標(biāo)識(shí)符 &引用名=目標(biāo)變量名; 例 1 int a ; const int &ra=a; ra=1; //錯(cuò)誤 a=1; //正確 例 2 string foo( ); void bar(string & s); 那么下面的表達(dá)式將是非法的: bar(foo( ));bar("hello world"); 原因在于 foo( )和"hello world"串都會(huì)產(chǎn)生一個(gè)臨時(shí)對(duì)象,而在 C++中,這些臨時(shí)對(duì)象都是const 類(lèi)型的。因此上面的表達(dá)式就是試圖將一個(gè) const 類(lèi)型的對(duì)象轉(zhuǎn)換為非 const 類(lèi)型,這是非法的。 引用型參數(shù)應(yīng)該在能被定義為 const 的情況下,盡量定義為 const 。 5. 將“引用”作為函數(shù)返回值類(lèi)型的格式、好處和需要遵守的規(guī)則? 格式:類(lèi)型標(biāo)識(shí)符 &函數(shù)名(形參列表及類(lèi)型說(shuō)明){ //函數(shù)體 } 好處:在內(nèi)存中不產(chǎn)生被返回值的副本;(注意:正是因?yàn)檫@點(diǎn)原因,所以返回一個(gè)局部變量的引用是不可取的。因?yàn)殡S著該局部變量生存期的結(jié)束,相應(yīng)的引用也會(huì)失效,產(chǎn)生runtime error! 注意事項(xiàng): (1)不能返回局部變量的引用。這條可以參照 Effective C++的 Item 31。主要原因是局部變量會(huì)在函數(shù)返回后被銷(xiāo)毀,因此被返回的引用就成為了"無(wú)所指"的引用,程序會(huì)進(jìn)入未知狀態(tài)。 (2)不能返回函數(shù)內(nèi)部 new 分配的內(nèi)存的引用。這條可以參照 Effective C++的 Item 31。 雖然不存在局部變量的被動(dòng)銷(xiāo)毀問(wèn)題,可對(duì)于這種情況(返回函數(shù)內(nèi)部 new 分配內(nèi)存的引用),又面臨其它尷尬局面。例如,被函數(shù)返回的引用只是作為一個(gè)臨時(shí)變量出現(xiàn),而沒(méi)有被賦予一個(gè)實(shí)際的變量,那么這個(gè)引用所指向的空間(由new分配)就無(wú)法釋放,造成memory leak。 (3)可以返回類(lèi)成員的引用,但最好是 const。這條原則可以參照 Effective C++的 Item 30。 主要原因是當(dāng)對(duì)象的屬性是與某種業(yè)務(wù)規(guī)則(business rule)相關(guān)聯(lián)的時(shí)候,其賦值常常與某些其它屬性或者對(duì)象的狀態(tài)有關(guān),因此有必要將賦值操作封裝在一個(gè)業(yè)務(wù)規(guī)則當(dāng)中。如果其它對(duì)象可以獲得該屬性的非常量引用(或指針),那么對(duì)該屬性的單純賦值就會(huì)破壞業(yè)務(wù)規(guī)則的完整性。 (4)流操作符重載返回值申明為“引用”的作用: 流操作符<<和>>,這兩個(gè)操作符常常希望被連續(xù)使用,例如:cout << "hello" << endl; 因此這兩個(gè)操作符的返回值應(yīng)該是一個(gè)仍然支持這兩個(gè)操作符的流引用?蛇x的其它方案包括:返回一個(gè)流對(duì)象和返回一個(gè)流對(duì)象指針。但是對(duì)于返回一個(gè)流對(duì)象,程序必須重新(拷貝)構(gòu)造一個(gè)新的流對(duì)象,也就是說(shuō),連續(xù)的兩個(gè)<<操作符實(shí)際上是針對(duì)不同對(duì)象的!這無(wú)法讓人接受。對(duì)于返回一個(gè)流指針則不能連續(xù)使用<<操作符。因此,返回一個(gè)流對(duì)象引用是惟一選擇。這個(gè)唯一選擇很關(guān)鍵,它說(shuō)明了引用的重要性以及無(wú)可替代性,也許這就是 C++語(yǔ)言中引入引用這個(gè)概念的原因吧。 賦值操作符=。這個(gè)操作符象流操作符一樣,是可以連續(xù)使用的,例如:x = j = 10;或者(x=10)=100;賦值操作符的返回值必須是一個(gè)左值,以便可以被繼續(xù)賦值。因此引用成了這個(gè)操作符的惟一返回值選擇。 例 3 #i nclude int &put(int n); int vals[10]; int error=-1; void main() { put(0)=10; //以 put(0)函數(shù)值作為左值,等價(jià)于 vals[0]=10; put(9)=20; //以 put(9)函數(shù)值作為左值,等價(jià)于 vals[9]=20; cout< } int &put(int n) { if (n>=0 && n<=9 ) return vals[n]; else { cout<<"subscript error"; return error; } } (5)在另外的一些操作符中,卻千萬(wàn)不能返回引用:+-/ 四則運(yùn)算符。它們不能返回引用,Effective C++的 Item23 詳細(xì)的討論了這個(gè)問(wèn)題。主要原因是這四個(gè)操作符沒(méi)有 side effect,因此,它們必須構(gòu)造一個(gè)對(duì)象作為返回值,可選的方案包括:返回一個(gè)對(duì)象、返回一個(gè)局部變量的引用,返回一個(gè) new 分配的對(duì)象的引用、返回一個(gè)靜態(tài)對(duì)象引用。根據(jù)前面提到的引用作為返回值的三個(gè)規(guī)則,第 2、3 兩個(gè)方案都被否決了。靜態(tài)對(duì)象的引用又因?yàn)?(a+b) ==(c+d))會(huì)永遠(yuǎn)為 true 而導(dǎo)致錯(cuò)誤。所以可選的只剩下返回一個(gè)對(duì)象了。 6. “引用”與多態(tài)的關(guān)系? 引用是除指針外另一個(gè)可以產(chǎn)生多態(tài)效果的手段。這意味著,一個(gè)基類(lèi)的引用可以指向它的派生類(lèi)實(shí)例。 例 4 Class A; Class B : Class A{...}; B b; A& ref = b; 7. “引用”與指針的區(qū)別是什么? 指針通過(guò)某個(gè)指針變量指向一個(gè)對(duì)象后,對(duì)它所指向的變量間接操作。程序中使用指針,程序的可讀性差;而引用本身就是目標(biāo)變量的別名,對(duì)引用的操作就是對(duì)目標(biāo)變量的操作。此外,就是上面提到的對(duì)函數(shù)傳 ref 和 pointer 的區(qū)別。 8. 什么時(shí)候需要“引用”? 流操作符<<和>>、賦值操作符=的返回值、拷貝構(gòu)造函數(shù)的參數(shù)、賦值操作符=的參數(shù)、其它情況都推薦使用引用。 以上 2-8 參考:http://blog.csdn.net/wfwd/archive/2006/05/30/763551.aspx 9. 結(jié)構(gòu)與聯(lián)合有和區(qū)別? 1. 結(jié)構(gòu)和聯(lián)合都是由多個(gè)不同的數(shù)據(jù)類(lèi)型成員組成, 但在任何同一時(shí)刻, 聯(lián)合中只存放了一個(gè)被選中的成員(所有成員共用一塊地址空間), 而結(jié)構(gòu)的所有成員都存在(不同成員的存放地址不同)。 2. 對(duì)于聯(lián)合的不同成員賦值, 將會(huì)對(duì)其它成員重寫(xiě), 原來(lái)成員的值就不存在了, 而對(duì)于結(jié)構(gòu)的不同成員賦值是互不影響的。 10. 下面關(guān)于“聯(lián)合”的題目的輸出? a) #i nclude union { int i; char x; }a; void main() { a.x[0] = 10;a.x = 1; printf("%d",a.i); } 答案:266 (低位低地址,高位高地址,內(nèi)存占用情況是 Ox010A) b) main() { union{ /定義一個(gè)聯(lián)合/ int i; struct{ /在聯(lián)合中定義一個(gè)結(jié)構(gòu)/ char first; char second; }half; }number; number.i=0x4241; /聯(lián)合成員賦值/ printf("%c%c\n", number.half.first, mumber.half.second); number.half.first='a'; /聯(lián)合中結(jié)構(gòu)成員賦值/ number.half.second='b'; printf("%x\n", number.i); getch(); } 答案:AB (0x41 對(duì)應(yīng)'A',是低位;Ox42 對(duì)應(yīng)'B',是高位) 6261 (number.i 和 number.half 共用一塊地址空間) 11. 已知 strcpy 的函數(shù)原型:char strcpy(char strDest, const char strSrc)其中 strDest 是目的字符串,strSrc 是源字符串。不調(diào)用 C++/C 的字符串庫(kù)函數(shù),請(qǐng)編寫(xiě)函數(shù) strcpy。 答案: char strcpy(char strDest, const char strSrc) { if ( strDest == NULL || strSrc == NULL) return NULL ; if ( strDest == strSrc) return strDest ; char tempptr = strDest ; while( (strDest++ = strSrc++) != \0 ) ; return tempptr ; } 12. 已知 String 類(lèi)定義如下: class String { public: String(const char str = NULL); // 通用構(gòu)造函數(shù) String(const String &another); // 拷貝構(gòu)造函數(shù)~ String(); // 析構(gòu)函數(shù) String & operater =(const String &rhs); // 賦值函數(shù) private: char m_data; // 用于保存字符串 }; 嘗試寫(xiě)出類(lèi)的成員函數(shù)實(shí)現(xiàn)。 答案: String::String(const char str) { if ( str == NULL ) //strlen 在參數(shù)為 NULL 時(shí)會(huì)拋異常才會(huì)有這步判斷 { m_data = new char ; m_data[0] = '\0' ; } else { m_data = new char[strlen(str) + 1]; strcpy(m_data,str); } } String::String(const String &another) { m_data = new char[strlen(another.m_data) + 1]; strcpy(m_data,other.m_data); } String& String::operator =(const String &rhs) { if ( this == &rhs) return this ; delete []m_data; //刪除原來(lái)的數(shù)據(jù),新開(kāi)一塊內(nèi)存 m_data = new char[strlen(rhs.m_data) + 1]; strcpy(m_data,rhs.m_data); return this ; } String::~String() { delete []m_data ; } 13. .h 頭文件中的 ifndef/define/endif 的作用? 答:防止該頭文件被重復(fù)引用。 14. #i nclude 與 #i nclude "file.h"的區(qū)別? 答:前者是從 Standard Library 的路徑尋找和引用 file.h,而后者是從當(dāng)前工作路徑搜尋并引用 file.h。