面向對象的程序設計思想是什么?
答:把數據結構和對數據結構進行操作的方法封裝形成一個個的對象。什么是類?
答:把一些具有共性的對象歸類后形成一個集合,也就是所謂的類。對象都具有的二方面特征是什么?分別是什么含義?
答:對象都具有的特征是:靜態特征和動態特征。
靜態特征是指能描述對象的一些屬性,動態特征是指對象表現出來的行為。在頭文件中進行類的聲明,在對應的實現文件中進行類的定義有什么意義?
答:這樣可以提高編譯效率,因為分開的話只需要編譯一次生成對應的.obj文件后,再次應用該類的地方,這個類就不會被再次編譯,從而大大提高了效率。在類的內部定義成員函數的函數體,這種函數會具備那種屬性?
答:這種函數會自動為內聯函數,這種函數在函數調用的地方在編譯階段都會進行代碼替換。成員函數通過什么來區分不同對象的成員數據?為什么它能夠區分?
答:通過this指針來區分的, 因為它指向的是對象的首地址。C++編譯器自動為類產生的四個缺省函數是什么?
答:默認構造函數,拷貝構造函數,析構函數,賦值函數。拷貝構造函數在哪幾種情況下會被調用?
答:
1.當類的一個對象去初始化該類的另一個對象時;
2.如果函數的形參是類的對象,調用函數進行形參和實參結合時;
3.如果函數的返回值是類對象,函數調用完成返回時。構造函數與普通函數相比在形式上有什么不同?(構造函數的作用,它的聲明形式來分析)
答:構造函數是類的一種特殊成員函數,一般情況下,它是專門用來初始化對象成員變量的。構造函數的名字必須與類名相同,它不具有任何類型,不返回任何值。什么時候必須重寫拷貝構造函數?
答:當構造函數涉及到動態存儲分配空間時,要自己寫拷貝構造函數,并且要深拷貝。構造函數的調用順序是什么?
答:
1.先調用基類構造函數;
2.按聲明順序初始化數據成員;
3.最后調用自己的構造函數。哪幾種情況必須用到初始化成員列表?
答:類的成員是常量成員初始化;
類的成員是對象成員初始化,而該對象沒有無參構造函數;
類的成員為引用時。什么是常對象?
答:常對象是指在任何場合都不能對其成員的值進行修改的對象。靜態函數存在的意義?
答:靜態私有成員在類外不能被訪問,可通過類的靜態成員函數來訪問;
當類的構造函數是私有的時,不像普通類那樣實例化自己,只能通過靜態成員函數來調用構造函數。在類外有什么辦法可以訪問類的非公有成員?
答:友元,繼承,公有成員函數。什么叫抽象類?
答:不用來定義對象而只作為一種基本類型用作繼承的類。運算符重載的意義?
答:為了對用戶自定義數據類型的數據的操作與內定義的數據類型的數據的操作形式一致。不允許重載的5個運算符是哪些?
答:
1. *(成員指針訪問運算符號);
2. ::域運算符;
3. sizeof 長度運算符號;
4. ?:條件運算符號;
5. .(成員訪問符)。
運算符重載的三種方式?
答:普通函數,友元函數,類成員函數。流運算符為什么不能通過類的成員函數重載?一般怎么解決?
答:因為通過類的成員函數重載必須是運算符的第一個是自己,而對流運算的重載要求第一個參數是流對象。一般通過友元來解決。賦值運算符和拷貝構造函數的區別與聯系?
答:
相同點:都是將一個對象copy到另一個中去。
不同點:拷貝構造函數涉及到要新建立一個對象。在哪種情況下要調用該類的析構函數?
答:對象生命周期結束時。對象間是怎樣實現數據的共享的?
答:通過類的靜態成員變量來實現的。靜態成員變量占有自己獨立的空間不為某個對象所私有。友元關系有什么特性?
答:單向的,非傳遞的,不能繼承的。對對象成員進行初始化的次序是什么?
答:它的次序完全不受它們在初始化表中次序的影響,只有成員對象在類中聲明的次序來
決定的。類和對象之間的關系是什么?
答:類是對象的抽象,對象是類的實例。對類的成員的訪問屬性有什么?
答:public,protected,private。const char *p與char * const p的區別?
答:如果const位于星號的左側,則const就是用來修飾指針所指向的變量,即指針指向為常量;
如果const位于星號的右側,const就是修飾指針本身,即指針本身是常量。是不是一個父類寫了一個virtual 函數,如果子類覆蓋它的函數不加virtual ,也能實
現多態?
virtual修飾符會被隱形繼承的。
virtual可加可不加,子類覆蓋它的函數不加virtual ,也能實現多態。函數重載是什么意思?它與虛函數的概念有什么區別?
函數重載是一個同名函數完成不同的功能,編譯系統在編譯階段通過函數參數個數、參數類型不同,函數的返回值來區分該調用哪一個函數,即實現的是靜態的多態性。但是記住:不能僅僅通過函數返回值不同來實現函數重載。而虛函數實現的是在基類中通過使用關鍵字virtual來申明一個函數為虛函數,含義就是該函數的功能可能在將來的派生類中定義或者在基類的基礎之上進行擴展,系統只能在運行階段才能動態決定該調用哪一個函數,所以實現的是動態的多態性。它體現的是一個縱向的概念,也即在基類和派生類間實現。構造函數和析構函數是否可以被重載,為什么?
答:構造函數可以被重載,析構函數不可以被重載。因為構造函數可以有多個且可以帶參數,而析構函數只能有一個,且不能帶參數。如何定義和實現一個類的成員函數為回調函數?
答:所謂的回調函數,就是預先在系統的對函數進行注冊,讓系統知道這個函數的存在,以后當某個事件發生時,再調用這個函數對事件進行響應。
定義一個類的成員函數時在該函數前加CALLBACK即將其定義為回調函數,函數的實現和普通成員函數沒有區別。虛函數是怎么實現的?
答:簡單說來使用了虛函數表。抽象類不會產生實例,所以不需要有構造函數。
答:錯。從一個模板類可以派生新的模板類,也可以派生非模板類。
答:對。main 函數執行以前,還會執行什么代碼?
答:全局對象的構造函數會在main 函數之前執行。當一個類A 中沒有生成任何成員變量與成員函數,這時sizeof(A)的值是多少,如果不是零,請解釋一下編譯器為什么沒有讓它為零。
答:肯定不是零。舉個反例,如果是零的話,聲明一個class A[10]對象數組,而每一個對象占用的空間是零,這時就沒辦法區分A[0],A[1]…了。delete與 delete []區別:
答:delete只會調用一次析構函數,而delete[]會調用每一個成員的析構函數。子類析構時要調用父類的析構函數嗎?
答:會調用,析構函數調用的次序是先派生類的析構后基類的析構,也就是說在基類的的析構調用的時候,派生類的信息已經全部銷毀了。繼承優缺點。
優點:
1、類繼承是在編譯時刻靜態定義的,且可直接使用,
2、類繼承可以較方便地改變父類的實現。
缺點:
1、因為繼承在編譯時刻就定義了,所以無法在運行時刻改變從父類繼承的實現
2、父類通常至少定義了子類的部分行為,父類的任何改變都可能影響子類的行為
3、如果繼承下來的實現不適合解決新的問題,則父類必須重寫或被其他更適合的類替換。這種依賴關系限制了靈活性并最終限制了復用性。解釋堆和棧的區別。
答:
棧區(stack):由編譯器自動分配釋放,存放函數的參數值,局部變量的值等。
堆:一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收。一個類的構造函數和析構函數什么時候被調用,是否需要手工調用?
答:構造函數在創建類對象的時候被自動調用,析構函數在類對象生命期結束時,由系統自動調用。何時需要預編譯:
答:總是使用不經常改動的大型代碼體。程序由多個模塊組成,所有模塊都使用一組標準的包含文件和相同的編譯選項。在這種情況下,可以將所有包含文件預編譯為一個預編譯頭。多態的作用?
主要是兩個:隱藏實現細節,使得代碼能夠模塊化;擴展代碼模塊,實現代碼重用;
接口重用:為了類在繼承和派生的時候,保證使用家族中任一類的實例的某一屬性時的正確調用。
虛擬函數與普通成員函數的區別?內聯函數和構造函數能否為虛擬函數?
答案:
區別:虛擬函數有virtual關鍵字,有虛擬指針和虛函數表,虛擬指針就是虛擬函數的接口,而普通成員函數沒有。內聯函數和構造函數不能為虛擬函數。構造函數和析構函數的調用順序? 析構函數為什么要虛擬?
答案:
構造函數的調用順序:基類構造函數—對象成員構造函數—派生類構造函數;析構函數的調用順序與構造函數相反。析構函數虛擬是為了防止析構不徹底,造成內存的泄漏。C++中類型為private的成員變量可以由哪些函數訪問?
答:只可以由本類中的成員函數和友員函數訪問。請說出類中private,protect,public三種訪問限制類型的區別
答:private是私有類型,只有本類中的成員函數訪問;protect是保護型的,本類和繼承類可以訪問;public是公有類型,任何類都可以訪問。類中成員變量怎么進行初始化?
答:可以通過構造函數的初始化列表或構造函數的函數體實現。在什么時候需要使用“常引用”?
答:如果既要利用引用提高程序的效率,又要保護傳遞給函數的數據不在函數中被改變,就應使用常引用。引用與指針有什么區別?
答:
- 引用必須被初始化,指針不必。
- 引用初始化以后不能被改變,指針可以改變所指的對象。
- 不存在指向空值的引用,但是存在指向空值的指針。
描述實時系統的基本特性
答:在特定時間內完成特定的任務,實時性與可靠性。全局變量和局部變量在內存中是否有區別?如果有,是什么區別?
答:全局變量儲存在靜態數據區,局部變量在堆棧中。堆棧溢出一般是由什么原因導致的?
答:沒有回收垃圾資源。什么函數不能聲明為虛函數?
答 構造函數(constructor)。IP地址的編碼分為哪倆部分?
答:IP地址由兩部分組成,網絡號和主機號。不能做switch()的參數類型是?
答:switch的參數不能為實型。如何引用一個已經定義過的全局變量?
答:可以用引用頭文件的方式,也可以用extern關鍵字,如果用引用頭文件方式來引用某個在頭文件中聲明的全局變理,假定你將那個變寫錯了,那么在編譯期間會報錯,如果你用extern方式引用時,假定你犯了同樣的錯誤,那么在編譯期間不會報錯,而在連接期間報錯。對于一個頻繁使用的短小函數,在C語言中應用什么實現,在C++中應用什么實現?
答:c用宏定義,c++用inline。C++是不是類型安全的?
答案:不是。兩個不同類型的指針之間可以強制轉換(用reinterpret cast)。簡述數組與指針的區別?
答:數組要么在靜態存儲區被創建(如全局數組),要么在棧上被創建。指針可以隨時指向任意類型的內存塊。
(1)修改內容上的區別
char a[] = “hello”;
a[0] = ‘X’;
char *p = “world”; // 注意p 指向常量字符串
p[0] = ‘X’; // 編譯器不能發現該錯誤,運行時錯誤
(2) 用運算符sizeof 可以計算出數組的容量(字節數)。sizeof(p),p 為指針得到的是一個指針變量的字節數,而不是p 所指的內存容量。
C++函數中值的傳遞方式
答:有三種方式:值傳遞、指針傳遞、引用傳遞。內存的分配方式
答:分配方式有三種:
1、 靜態存儲區,是在程序編譯時就已經分配好的,在整個運行期間都存在,如全局變量、常量。
2、 棧上分配,函數內的局部變量就是從這分配的,但分配的內存容易有限。
3、 堆上分配,也稱動態分配,如我們用new,malloc分配內存,用delete,free來釋放的內存。extern“C”有什么作用?
答:extern “C”是由C++提供的一個連接交換指定符號,用于告訴C++這段代碼是C函數。這是因為C++編譯后庫中函數名會變得很長,與C生成的不一致,造成C++不能直接調用C函數,加上extren “c”后,C++就能直接調用C函數了。用什么函數開啟新進程、線程。(Windows API)
答:線程:CreateThread/AfxBeginThread等;
進程:CreateProcess等。SendMessage和PostMessage有什么區別(Windows API)
答:SendMessage是阻塞的,等消息被處理后,代碼才能走到SendMessage的下一行。PostMessage是非阻塞的,不管消息是否已被處理,代碼馬上走到PostMessage的下一行。CMemoryState主要功能是什么?(Windows API)
答:查看內存使用情況,解決內存泄露問題。代碼#include <filename.h> 和 #include “filename.h” 有什么區別?
答:
對于#include <filename.h> ,編譯器從標準庫路徑開始搜索 filename.h
對于#include “filename.h” ,編譯器從用戶的工作路徑開始搜索 filename.h。處理器標識#error的目的是什么?
答:編譯時輸出一條錯誤信息,并中止編譯。代碼
#if!defined(AFX_…_HADE_H)
#define(AFX_…_HADE_H)
……
#endif
作用?
答:防止該頭文件被重復引用。
在定義一個宏的時候要注意什么?
答:定義部分的每個形參和整個表達式都必須用括號括起來,以避免不可預料的錯誤發生。數組在做函數實參的時候會轉變為什么類型?
答:數組在做實參時會變成指針類型。系統會自動打開和關閉的3個標準的文件是?
(1) 標準輸入----鍵盤---stdin
(2) 標準輸出----顯示器---stdout
(3) 標準出錯輸出----顯示器---stderr在Win32下 char, int, float, double各占多少位?
(1) char 占用8位;
(2) int 占用32位;
(3) float 占用32位;
(4) double 占用64位。strcpy()和memcpy()的區別?
答:strcpy()和memcpy()都可以用來拷貝字符串,strcpy()拷貝以‘\0’結束,但memcpy()必須指定拷貝的長度。說明define和const在語法和含義上有什么不同?
(1) #define是C語法中定義符號常量的方法,符號常量只是用來表達一個值,在編譯階段符號就被值替換了,它沒有類型。
(2) const是C++語法中定義常變量的方法,常變量具有變量特性,它具有類型,內存中存在以它命名的存儲單元,可以用sizeof測出長度。說出字符常量和字符串常量的區別,并使用運算符sizeof計算有什么不用?
答:字符常量是指單個字符;而字符串常量以‘\0’結束,使用運算符sizeof計算多占一字節的存儲空間。簡述全局變量的優缺點?
答:全局變量也稱為外部變量,它是在函數外部定義的變量,它屬于一個源程序文件,它保存上一次被修改后的值,便于數據共享,但不方便管理,易引起意想不到的錯誤。總結static的應用和作用?
(1)函數體內static變量的作用范圍為該函數體,不同于auto變量,該變量的內存只被分配一次,因此其值在下次調用時仍維持上次的值;
(2)在模塊內的static全局變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問;
(3)在模塊內的static函數只可被這一模塊內的其它函數調用,這個函數的使用范圍被限制在聲明它的模塊內;
(4)在類中的static成員變量屬于整個類所擁有,對類的所有對象只有一份拷貝;
(5)在類中的static成員函數屬于整個類所擁有,這個函數不接收this指針,因而只能訪問類的static成員變量。總結const的應用和作用?
(1)欲阻止一個變量被改變,可以使用const關鍵字。在定義該const變量時,通常需要對它進行初始化,因為以后就沒有機會再去改變它了;
(2)對指針來說,可以指定指針本身為const,也可以指定指針所指的數據為const,或二者同時指定為const;
(3)在一個函數聲明中,const可以修飾形參,表明它是一個輸入參數,在函數內部不能改變其值;
(4)對于類的成員函數,若指定其為const類型,則表明其是一個常函數,不能修改類的成員變量;
(5)對于類的成員函數,有時候必須指定其返回值為const類型,以使得其返回值不為“左值”。什么是指針?談談你對指針的理解?
答:指針是一個變量,該變量專門存放內存地址;指針變量的類型取決于其指向的數據類型,在所指數據類型前加*;指針變量的特點是它可以訪問所指向的內存。什么是常指針,什么是指向常變量的指針?
答:常指針的含義是該指針所指向的地址不能變,但該地址所指向的內容可以變化,使用常指針可以保證我們的指針不能指向其它的變量。指向常變量的指針是指該指針的變量本身的地址可以變化,可以指向其它的變量,但是它所指的內容不可以被修改。函數指針和指針函數的區別?
答:函數指針是指指向一個函數入口的指針;指針函數是指函數的返回值是一個指針類型。簡述Debug版本和Release版本的區別?
答:Debug版本是調試版本,Release版本是發布給用戶的最終非調試的版本。指針的幾種典型應用情況?
答:
int *p[n];-----指針數組,每個元素均為指向整型數據的指針。
int (*)p[n];------p為指向一維數組的指針,這個一維數組有n個整型數據。
int *p();----------函數帶回指針,指針指向返回的值。
int (*)p();------p為指向函數的指針。
static函數與普通函數有什么區別?
答:static函數在內存中只有一份,普通函數在每個被調用中維持一份拷貝。struct(結構) 和 union(聯合)的區別?
答:
1、結構和聯合都是由多個不同的數據類型成員組成, 但在任何同一時刻, 聯合中只存放了一個被選中的成員(所有成員共用一塊地址空間), 而結構的所有成員都存在(不同成員的存放地址不同)。
2、對于聯合的不同成員賦值, 將會對其它成員重寫, 原來成員的值就不存在了, 而對于結構的不同成員賦值是互不影響的。class 和 struct 的區別?
答:struct 的成員默認是公有的,而類的成員默認是私有的。簡述枚舉類型?
答:枚舉方便一次定義一組常量,使用起來很方便。assert()的作用?
答:assert()是一個調試程序時經常使用的宏,在程序運行時它計算括號內的表達式,如果表達式為FALSE (0), 程序將報告錯誤,并終止執行。如果表達式不為0,則繼續執行后面的語句。這個宏通常原來判斷程序中是否出現了明顯非法的數據,如果出現了終止程序以免導致嚴重后果,同時也便于查找錯誤。局部變量和全局變量是否可以同名?
答:能。局部會屏蔽全局。要用全局變量,需要使用"::"(域運算符)。程序變量在內存中的分布?
答:程序的局部變量存在于(堆棧)中,全局變量存在于(靜態區 )中,動態申請數據存在于( 堆)中。在什么時候使用常引用?
答:如果既要利用引用提高程序的效率,又要保護傳遞給函數的數據不在函數中被改變,就應使用常引用。類的聲明和實現的分開的好處?
1、起保護作用;
2、提高編譯的效率。windows消息系統由哪幾部分構成?
答:由一下3部分組成:
1、消息隊列:操作系統負責為進程維護一個消息隊列,程序運行時不斷從該消息隊列中獲取消息、處理消息;
2、消息循環:應用程序通過消息循環不斷獲取消息、處理消息。
3、消息處理:消息循環負責將消息派發到相關的窗口上使用關聯的窗口過程函數進行處理。什么是消息映射?
答:消息映射就是讓程序員指定MFC類(有消息處理能力的類)處理某個消息。然后由程序員完成對該處理函數的編寫,以實現消息處理功能。什么是UDP和TCP的區別是什么?
答:TCP的全稱為傳輸控制協議。這種協議可以提供面向連接的、可靠的、點到點的通信。UDP全稱為用戶報文協議,它可以提供非連接的不可靠的點到多點的通信。用TCP還是UDP,那要看你的程序注重哪一個方面?可靠還是快速?winsock建立連接的主要實現步驟?
答:服務器端:socket()建立套接字,綁定(bind)并監聽(listen),用accept()等待客戶端連接, accept()發現有客戶端連接,建立一個新的套接字,自身重新開始等待連接。該新產生的套接字使用send()和recv()寫讀數據,直至數據交換完畢,closesocket()關閉套接字。
客戶端:socket()建立套接字,連接(connect)服務器,連接上后使用send()和recv(),在套接字上寫讀數據,直至數據交換完畢,closesocket()關閉套接字。進程間主要的通訊方式?
答:信號量,管道,消息,共享內存。構成Win32 API 函數的三個動態鏈接庫是什么?
答:內核庫,用戶界面管理庫,圖形設備界面庫。創建一個窗口的步驟是?
答:填充一個窗口類結構->注冊這個窗口類->然后再創建窗口->顯示窗口->更新窗口。模態對話框和非模態對話框有什么區別?
答:
1.調用規則不同:前者是用DoModal()調用,后者通過屬性和ShowWindow()來顯示。
2.模態對話框在沒有關閉前用戶不能進行其他操作,而非模態對話框可以。
3.非模態對話框創建時必須編寫自己的公有構造函數,還要調用Create()函數。從EDIT框中取出數據給關聯的變量,已經把關聯的變量的數據顯示在EDIT框上的函數是什么?
答: UpdateData(TRUE), Updatedata(FALSE).簡單介紹GDI?
答;GDI 是Graphics Device Interface 的縮寫,譯為:圖形設備接口;是一個在Windows應用程序中執行與設備無關的函數庫,這些函數在不同的輸出設備上產生圖形以及文字輸出。windows消息分為幾類?并對各類做簡單描述。
1、窗口消息:與窗口相關的消息,除WM_COMMAND之外的所有以WM_開頭的消息;
2、命令消息;用于處理用戶請求,以WM_COMMAND表示的消息;
3、控件通知消息:統一由WM_NOTIFT表示;
4、用戶自定義消息。如何自定義消息?
答:使用WM_USER 和WM_APP兩個宏來自定義消息。簡述Visual C++ 、Win32 API和MFC之間的關系?
答:
(1) Visual C+是一個以C++程序設計語言為基礎的、集成的、可視化的編程環境;
(2) Win32 API是32位Windows操作系以C/C++形式提供的一組應用程序接口;
(3) MFC是對Win32 API的封裝,簡化了開發過程。怎樣消除多重繼承中的二義性?
答:
1、成員限定符
2、虛基類什么叫靜態關聯,什么叫動態關聯?
答:在多態中,如果程序在編譯階段就能確定實際執行動作,則稱靜態關聯,如果等到程序運行才能確定叫動態關聯。多態的兩個必要條件?
答:
1、一個基類的指針或引用指向一個派生類對象;
2、虛函數。什么叫智能指針?
答:當一個類中,存在一個指向另一個類對象的指針時,對指針運算符進行重載,那么當前類對象可以通過指針像調用自身成員一樣調用另一個類的成員。什么時候需要用虛析構函數?
答:當基類指針指向用new運算符生成的派生類對象時,delete基類指針時,派生類部分沒有釋放掉而造成釋放不徹底現象,需要虛析構函數。MFC中,大部分類是從哪個類繼承而來?
答:Cobject。什么是平衡二叉樹?
答:左右子樹都是平衡二叉樹,而且左右子樹的深度差值的約對值不大于1。語句for( ;1 ;)有什么問題?它是什么意思?
答:無限循環,和while(1)相同。派生新類的過程要經歷三個步驟
答:
1、吸收基類成員
2、改造基類成員
3、添加新成員TCP/IP 建立連接的過程
答:在TCP/IP協議中,TCP協議提供可靠的連接服務,采用三次握手建立一個連接。
第一次握手:建立連接時,客戶端發送連接請求到服務器,并進入SYN_SEND狀態,等
待服務器確認;
第二次握手:服務器收到客戶端連接請求,向客戶端發送允許連接應答,此時服務器進入
SYN_RECV狀態;
第三次握手:客戶端收到服務器的允許連接應答,向服務器發送確認,客戶端和服務器進
入通信狀態,完成三次握手。memset ,memcpy 的區別
答:
memset用來對一段內存空間全部設置為某個字符,一般用在對定義的字符串進行初始化為'\0'。
memcpy用來做內存拷貝,你可以拿它拷貝任何數據類型的對象,可以指定拷貝的數據長度;在C++ 程序中調用被 C 編譯器編譯后的函數,為什么要加 extern “C”?
答:C++語言支持函數重載,C 語言不支持函數重載。函數被C++編譯后在庫中的
名字與C 語言的不同。假設某個函數的原型為: void foo(int x, int y);該函數被C 編譯器編譯后在庫中的名字為_foo , 而C++編譯器則會產生像_foo_int_int 之類的名字。C++提供了C 連接交換指定符號extern“C”來解決名字匹配問題。怎樣定義一個純虛函數?含有純虛函數的類稱為什么?
答:在虛函數的后面加=0,含有虛函數的類稱為抽象類。已知strcpy函數的原型是:char * strcpy(char * strDest,const char * strSrc);不調用庫函數,實現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 ;
}
- 已知類String 的原型為:
class String
{
public:
String(const char *str = NULL); // 普通構造函數
String(const String &other); // 拷貝構造函數
~ String(void); // 析構函數
String & operate =(const String &other); // 賦值函數
private:
char *m_data; // 用于保存字符串
};
請編寫String 的上述4 個函數。
答:
String::String(const char *str)
{
if ( str == NULL ) //strlen在參數為NULL時會拋異常才會有這步判斷
{
m_data = new char[1] ;
m_data[0] = '' ;
}
else
{
m_data = new char[strlen(str) + 1];
strcpy(m_data,str);
}
}
String::String(const String &other)
{
m_data = new char[strlen(other.m_data) + 1];
strcpy(m_data,other.m_data);
}
String & String::operator =(const String &other)
{
if ( this == &other)
return *this ;
delete []m_data;
m_data = new char[strlen(other.m_data) + 1];
strcpy(m_data,other.m_data);
return *this ;
}
String::~ String(void)
{
delete []m_data ;
}
類成員函數的重載、覆蓋和隱藏區別?
答:
成員函數被重載的特征:
(1)相同的范圍(在同一個類中);
(2)函數名字相同;
(3)參數不同;
(4)virtual 關鍵字可有可無。
覆蓋是指派生類函數覆蓋基類函數,特征是:
(1)不同的范圍(分別位于派生類與基類);
(2)函數名字相同;
(3)參數相同;
(4)基類函數必須有virtual 關鍵字。
“隱藏”是指派生類的函數屏蔽了與其同名的基類函數,規則如下:
(1)如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵
字,基類的函數將被隱藏(注意別與重載混淆)。
(2)如果派生類的函數與基類的函數同名,并且參數也相同,但是基類函數沒有virtual
關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。如何打印出當前源文件的文件名以及源文件的當前行號?
答:
cout << __FILE__ ;
cout<<__LINE__ ;
__FILE__和__LINE__是系統預定義宏,這種宏并不是在某個文件中定義的,而是由編譯器定義的。
- 文件中有一組整數,要求排序后輸出到另一個文件中
答:
void Order(vector<int> &data) //起泡排序
{
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 )
{
vector<int>data;
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<<data[i]<<" ";
out.close();
}
- 一個鏈表的結點結構
struct Node
{
int data ;
Node *next ;
};
typedef struct Node Node ;
已知鏈表的頭結點head,寫一個函數把這個鏈表逆序
答:
Node * ReverseList(Node *head) //鏈表逆序
{
if ( head == NULL || head->next == NULL )
return head;
Node *p1 = head ;
Node *p2 = p1->next ;
Node *p3 = p2->next ;
p1->next = NULL ;
while ( p3 != NULL )
{
p2->next = p1 ;
p1 = p2 ;
p2 = p3 ;
p3 = p3->next ;
}
p2->next = p1 ;
head = p2 ;
return head ;
}
- 一個鏈表的結點結構
struct Node
{
int data ;
Node *next ;
};
typedef struct Node Node ;
已知兩個鏈表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 ;
}
- 已知兩個鏈表head1 和head2 各自有序,請把它們合并成一個鏈表依然有序,這次
要求用遞歸方法進行。
答:
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 ;
}
133.分析一下這段程序的輸出
#include <iostream>
using namespace std;
class B
{
public:
B()
{
cout<<"default constructor"<<endl;
}
~B()
{
cout<<"destructed"<<endl;
}
B(int i):data(i)
{
cout<<"constructed by parameter" << data <<endl;
}
private:
int data;
};
B Play( B b)
{
return b ;
}
int main(int argc, char* argv[])
{
B temp = Play(5);
return 0;
}
將“引用”作為函數參數有哪些特點?
(1)傳遞引用給函數與傳遞指針的效果是一樣的。這時,被調函數的形參就成為原來主調
函數中的實參變量或對象的一個別名來使用,所以在被調函數中對形參變量的操作就是對
其相應的目標對象(在主調函數中)的操作。
(2)使用引用傳遞函數的參數,在內存中并沒有產生實參的副本,它是直接對實參操作;
而使用一般變量傳遞函數的參數,當發生函數調用時,需要給形參分配存儲單元,形參變
量是實參變量的副本;如果傳遞的是對象,還將調用拷貝構造函數。因此,當參數傳遞的數
據較大時,用引用比用一般變量傳遞參數的效率和所占空間都好。
(3)使用指針作為函數的參數雖然也能達到與使用引用的效果,但是,在被調函數中同
樣要給形參分配存儲單元,且需要重復使用"*指針變量名"的形式進行運算,這很容易產生
錯誤且程序的閱讀性較差;另一方面,在主調函數的調用點處,必須用變量的地址作為實
參。而引用更容易使用,更清晰。什么時候需要“引用”?
流操作符(<<、>>)和賦值操作符(=)的返回值、拷貝構造函數的參數、賦值操作符的
參數、其它情況都推薦使用引用。面向對象的三個基本特征,并簡單敘述?
封裝:將客觀事物抽象成類,每個類對自身的數據和方法實行protection(private,
protected,public)繼承:廣義的繼承有三種實現形式:實現繼承(指使用基類的屬性和方法而無需額外編
碼的能力)、可視繼承(子窗體使用父窗體的外觀和實現代碼)、接口繼承(僅使用屬性和
方法,實現滯后到子類實現)。前兩種(類繼承)和后一種(對象組合=>接口繼承以及純
虛函數)構成了功能復用的兩種方式。多態:是將父對象設置成為和一個或更多的他的子對象相等的技術,賦值之后,父對象
就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許
將子類類型的指針賦值給父類類型的指針。求下面函數的返回值
int func(x)
{
int countx = 0;
while(x)
{
countx ++;
x = x&(x-1);
}
return countx;
}
假定x = 9999。 答案:8
思路:將x轉化為2進制,看含有的1的個數。
- 寫出下列代碼的輸出內容
#include<stdio.h>
int inc(int a)
{
return(++a);
}
int multi(int*a,int*b,int*c)
{
return(*c=*a**b);
}
typedef int(FUNC1)(int in);
typedef int(FUNC2) (int*,int*,int*);
void show(FUNC2 fun,int arg1, int*arg2)
{
INCp=&inc;
int temp =p(arg1);
fun(&temp,&arg1, arg2);
printf("%d\n",*arg2);
}
main()
{
int a;
show(multi,10,&a);
return 0;
}
答:110
- 編寫一個 C 函數,該函數在一個字符串中找到可能的最長的子字符串,且該字符串
是由同一字符組成的。
char * search(char *cpSource, char ch)
{
char *cpTemp=NULL, *cpDest=NULL;
int iTemp, iCount=0;
while(*cpSource)
{
if(*cpSource == ch)
{
iTemp = 0;
cpTemp = cpSource;
while(*cpSource == ch)
++iTemp, ++cpSource;
if(iTemp > iCount)
iCount = iTemp, cpDest = cpTemp;
if(!*cpSource)
break;
}
++cpSource;
}
return cpDest;
}
- 請編寫一個 C 函數,該函數在給定的內存區域搜索給定的字符,并返回該字符所在
位置索引值。
int search(char *cpSource, int n, char ch)
{
int i;
for(i=0; i<n && *(cpSource+i) != ch; ++i);
return i;
}
一個單向鏈表,不知道頭節點,一個指針指向其中的一個節點,問如何刪除這個指針指
向的節點?
將這個指針指向的next節點值copy到本節點,將next指向next->next,并隨后刪除原next指向的節點。用指針的方法,將字符串“ABCD1234efgh”前后對調顯示
#include <stdio.h>
#include <string.h>
#include <dos.h>
int main()
{
char str[] = "ABCD1234efgh";
int length = strlen(str);
char * p1 = str;
char * p2 = str + length - 1;
while(p1 < p2)
{
char c = *p1;
*p1 = *p2;
*p2 = c;
++p1;
--p2;
}
printf("str now is %s\n",str);
system("pause");
return 0;
}
142、有一分數序列:1/2,1/4,1/6,1/8……,用函數調用的方法,求此數列前20項的和?
#include <stdio.h>
double getValue()
{
double result = 0;
int i = 2;
while(i < 42)
{
result += 1.0 / i;//一定要使用1.0做除數,不能用1,否則結果將自動轉化成整數,即0.000000
i += 2;
}
return result;
}
int main()
{
printf("result is %f\n", getValue());
system("pause");
return 0;
}
- 有一個數組a[1000]存放0--1000;要求每隔二個數刪掉一個數,到末尾時循環至開頭繼續進行,求最后一個被刪掉的數的原始下標位置。
以7個數為例:
{0,1,2,3,4,5,6,7} 0-->1-->2(刪除)-->3-->4-->5(刪除)-->6-->7-->0(刪除),
如此循環直到最后一個數被刪除。
方法1:數組
#include <iostream>
using namespace std;
#define null 1000
int main()
{
int arr[1000];
for (int i=0;i<1000;++i)
arr[i]=i;
int j=0;
int count=0;
while(count<999)
{
while(arr[j%1000]==null)
j=(++j)%1000;
j=(++j)%1000;
while(arr[j%1000]==null)
j=(++j)%1000;
j=(++j)%1000;
while(arr[j%1000]==null)
j=(++j)%1000;
arr[j]=null;
++count;
}
while(arr[j]==null)
j=(++j)%1000;
cout<<j<<endl;
return 0;
}
方法2:鏈表
#include<iostream>
using namespace std;
#define null 0
struct node
{
int data;
node* next;
};
int main()
{
node* head=new node;
head->data=0;
head->next=null;
node* p=head;
for(int i=1;i<1000;i++)
{
node* tmp=new node;
tmp->data=i;
tmp->next=null;
head->next=tmp;
head=head->next;
}
head->next=p;
while(p!=p->next)
{
p->next->next=p->next->next->next;
p=p->next->next;
}
cout<<p->data;
return 0;
}
方法3:通用算法
#include <stdio.h>
#define MAXLINE 1000 //元素個數
/*
MAXLINE 元素個數
a[] 元素數組
R[] 指針場
suffix 下標
index 返回最后的下標序號
values 返回最后的下標對應的值
start 從第幾個開始
K 間隔
*/
int find_n(int a[],int R[],int K,int& index,int& values,int s=0) {
int suffix;
int front_node,current_node;
suffix=0;
if(s==0) {
current_node=0;
front_node=MAXLINE-1;
}
else {
current_node=s;
front_node=s-1;
}
while(R[front_node]!=front_node) {
printf("%d\n",a[current_node]);
R[front_node]=R[current_node];
if(K==1) {
current_node=R[front_node];
continue;
}
for(int i=0;i<K;i++){
front_node=R[front_node];
}
current_node=R[front_node];
}
index=front_node;
values=a[front_node];
return 0;
}
int main(void) {
int a[MAXLINE],R[MAXLINE],suffix,index,values,start,i,K;
suffix=index=values=start=0;
K=2;
for(i=0;i<MAXLINE;i++) {
a[i]=i;
R[i]=i+1;
}
R[i-1]=0;
find_n(a,R,K,index,values,2);
printf("the value is %d,%d\n",index,values);
return 0;
}
- 指出下列程序有什么錯誤:
void test2()
{
char string[10], str1[10];
int i;
for(i=0; i<10; i++)
{
str1[i] = 'a';
}
strcpy( string, str1 );
}
解答:指出字符數組str1不能在數組內結束;指出strcpy(string, str1)調用使得從str1內存起復制到string內存起所復制的字節數具有不確定性,在此基礎上指出庫函數strcpy工作方式;
str1不能在數組內結束:因為str1的存儲為:{a,a,a,a,a,a,a,a,a,a},沒有'\0'(字符串結束符),所以不能結束strcpy( char *s1,char *s2)他的工作原理是,掃描s2指向的內存,逐個字符付到s1所指向的內存,直到碰到'\0',因為str1結尾沒有'\0',所以具有不確定性。
正確應如下:
void test2()
{
char string[10], str1[10];
int i;
for(i=0; i<9; i++)
{
str1[i] = 'a'+i; //把abcdefghi賦值給字符數組
}
str[i]='\0';//加上結束符
strcpy( string, str1 );
}
- 實現strcmp
int StrCmp(const char *str1, const char *str2)
{
assert(str1 && srt2);
while(*str1 && *str1++ = = *str2++);
return *str1-*str2;
}
- 字符串A和B,輸出A和B中的最大公共子串。
比如A="aocdfe" B="pmcdfa" 則輸出"cdf"
#include<iostream>
#include<string>
#include<stdlib.h>
using namespace std;
string iQueryMaxCommString(string str1, string str2)
{
/* 在這里實現功能,將結果填入輸入數組中*/
int pos=0,maxlen=0;
string temp;
for(int i=0;i<str2.size();i++)
{
for(int j=str2.size()-i; j>0; j--)
{
temp=str2.substr (i,j);
if((str1.find(temp)!=string::npos)&&maxlen < temp.size())
{
maxlen=temp.size();
pos=i;
}
}
}
if(maxlen==0)
return NULL;
else
return str2.substr (pos,maxlen);
}
int main()
{
string str1,str2,temp;
getline(cin,str1);
getline(cin,str2);
string output;
if(str1.size ()<str2.size ())
{
temp=str1;
str1=str2;
str2=temp;
}
output=iQueryMaxCommString( str1, str2);
cout << output;
return 0;
}
- 寫一個函數比較兩個字符串str1和str2的大小,若相等返回0,若str1大于str2返回1,若str1小于str2返回-1
int strcmp ( const char * src,const char * dst)
{
int ret = 0 ;
while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
{
++src;
++dst;
}
if ( ret < 0 )
ret = -1 ;
else if ( ret > 0 )
ret = 1 ;
return( ret );
}
148、判斷一個字符串是不是回文
int IsReverseStr(char *aStr)
{
int i,j;
int found=1;
if(aStr==NULL)
return -1;
j=strlen(aStr);
for(i=0;i<j/2;i++)
if(*(aStr+i)!=*(aStr+j-i-1))
{
found=0;
break;
}
return found;
}
- 下列代碼中
#include <stdio.h>
int main()
{
int c[3][3]={1,2,3,4,5,6,7,8,9};
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
printf("%ld\n",&c[j]);
printf("-------------------------\n");
printf("%ld\n",(c+1));
printf("%ld\n",(*c+1));
printf("%ld\n",&c[0][0]);
printf("%ld\n",**c);
printf("%ld\n",*c[0]);
if(int(c)==int(*c)) printf("equl");
}
為么c,c的值相等;(c+1),(c+1)的值不等; c,c,c,代表什么意思?
答:
c是第一個元素的地址,c是第一行元素的首地址,其實第一行元素的地址就是第一個元
素的地址,這容易理解。c是提領第一個元素。
為什么c,c的值相等?
int c因為直接用c表示數組c[0][0] printf("%ld\n",c[0]);語句已將指針移到數組頭。
int(*c)表示c0的值為1,所以相等。 數組c的存放空間示意如下:(機器中是行優先存放
的) c[0][0] c[0][1] c[0][2] c[1][0] c[1][1] c[1][2] c[2][0] c[2][1] c[2][2] c 是一個二維數組名,實際上它是一個指針常量,不能進行自加、自減運算,即:c++、c--、++c、--c 都是不允許的;
c: 數組名;是一個二維指針,它的值就是數組的首地址,也即第一行元素的首地址(等于 c),也 等于第一行第一個元素的地址( & c[0][0]);可以說成是二維數組的行指針。 c: 第一行元素的首地址;是一個一維指針,可以說成是二維數組的列指針。
c:二維數組中的第一個元素的值;即:c[0][0] 所以: c 和 c的值是相等的,但他們兩者不能相互賦值,(類型不同); (c + 1) :c是行指針,(c + 1)是在c的基礎上加上二維數組一行的地址長度,即從&c[0][0] 變到了&c[1][0];
(c + 1):c是列指針,(c + 1)是在c的基礎上加上二數組一個元素的所占的長度,即從 &c[0][0]變到了
&c[0][1] 從而(c + 1)和(c + 1)的值就不相等了。
定義 int a[3][4], 則變量占有的內存空間為:_____
參考答案:
int p; /16位下sizeof(p)=2, 32位下sizeof(p)=4/ 總共 34sizeof(p)寫出判斷ABCD四個表達式的是否正確, 若正確, 寫出經過表達式中 a的值
int a = 4;
(A)a += (a++); (B) a += (++a) ; (C) (a++) += a; (D) (++a) += (a++);
a = ?
答:C錯誤,左側不是一個有效變量,不能賦值,可改為(++a) += a;
改后答案依次為9,10,10,11某32位系統下, C++程序,請計算sizeof 的值
char str[] = “http://www.ibegroup.com/”
char *p = str ;
int n = 10;
請計算
(1)sizeof (str ) = ?
(2)sizeof ( p ) = ?
(3)sizeof ( n ) = ?
void Foo ( char str[100]){
請計算
sizeof( str ) = ?(4)
}
void *p = malloc( 100 );
請計算
sizeof ( p ) = ?(5)
答:(1)17 (2)4 (3) 4 (4)4 (5)4
- 回答下面的問題
void GetMemory(char **p, int num){
*p = (char *)malloc(num);
}
void Test(void){
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
請問運行Test 函數會有什么樣的結果?
答:輸出“hello”
- 下面代碼
void Test(void){
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL){
strcpy(str, “world”);
printf(str);
}
}
請問運行Test 函數會有什么樣的結果?
答:輸出“world”
- 下面代碼
char *GetMemory(void){
char p[] = "hello world";
return p;
}
void Test(void){
char *str = NULL;
str = GetMemory();
printf(str);
}
請問運行Test 函數會有什么樣的結果?
答:無效的指針,輸出不確定
- 編寫strcat函數
已知strcat函數的原型是char *strcat (char *strDest, const char *strSrc);
其中strDest 是目的字符串,strSrc 是源字符串。
(1)不調用C++/C 的字符串庫函數,請編寫函數 strcat
答:
VC源碼:
char * __cdecl strcat (char * dst, const char * src)
{
char * cp = dst;
while( *cp )
cp++;
while( *cp++ = *src++ ) ;
return( dst );
}
strcat能把strSrc 的內容連接到strDest,為什么還要char * 類型的返回值?
答:方便賦值給其他變量MFC中CString是類型安全類么?
答:不是,其它數據類型轉換到CString可以使用CString的成員函數Format來轉換。C++中什么數據分配在棧或堆中?
答:
棧: 存放局部變量,函數調用參數,函數返回值,函數返回地址。由系統管理
堆: 程序運行時動態申請,new 和malloc申請的內存就在堆上函數模板與類模板有什么區別?
答:函數模板的實例化是由編譯程序在處理函數調用時自動完成的,而類模板的實例化
必須由程序員在程序中顯式地指定。int i=10, j=10, k=3; k=i+j; k最后的值是?
答:60,此題考察優先級,實際寫成: k=(i+j);,賦值運算符優先級最低do……while和while……do有什么區別?
答:前一個循環一遍再判斷,后一個判斷以后再循環
163、請寫出下列代碼的輸出內容
#include <stdio.h>
int main()
{
int a,b,c,d;
a=10;
b=a++;
c=++a;
d=10*a++;
printf("b,c,d:%d,%d,%d",b,c,d);
return 0;
}
答 、10,12,120
164.在c語言庫函數中將一個字符轉換成整型的函數是atol()嗎,這個函數的原型是什么?
答 、函數名: atol
功 能: 把字符串轉換成長整型數
用 法: long atol(const char *nptr);
程序例:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
long l;
char *str = "98765432";
l = atol(str);
printf("string = %s integer = %ld\n", str, l);
return(0);
}
- 以下三條輸出語句分別輸出什么?
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char* str5 = "abc";
const char* str6 = "abc";
cout << boolalpha << ( str1==str2 ) << endl; // 輸出什么?
cout << boolalpha << ( str3==str4 ) << endl; // 輸出什么?
cout << boolalpha << ( str5==str6 ) << endl; // 輸出什么?
答:分別輸出false,false,true。str1和str2都是字符數組,每個都有其自己的存儲區,它們的值則是各存儲區首地址,不等;str3和str4同上,只是按const語義,它們所指向的數據區不能修改。str5 和str6 并非數組而是字符指針,并不分配存儲區,其后的“abc”以常量形式存于靜態數據區,而它們自己僅是指向該區首地址的指針,相等。
- 以下代碼中的兩個sizeof用法有問題嗎?
void UpperCase( char str[] ) // 將 str 中的小寫字母轉換成大寫字母
{
for( size_t i=0; i<sizeof(str)/sizeof(str[0]); ++i )
if( 'a'<=str[i] && str[i]<='z' )
str[i] -= ('a'-'A' );
}
char str[] = "aBcDe";
cout << "str字符長度為: " << sizeof(str)/sizeof(str[0]) << endl;
UpperCase( str );
cout << str << endl;
答:函數內的sizeof有問題。根據語法,sizeof如用于數組,只能測出靜態數組的大小,無法檢測動態分配的或外部數組大小。函數外的str是一個靜態定義的數組,因此其大小為6,函數內的str實際只是一個指向字符串的指針,沒有任何額外的與數組相關的信息,因此sizeof作用于上只將其當指針看,一個指針為4個字節,因此返回4。
非C++內建型別 A 和 B,在哪幾種情況下B能隱式轉化為A?
答:
a. class B : public A { ……} // B公有繼承自A,可以是間接繼承的
b. class B { operator A( ); } // B實現了隱式轉化為A的轉化
c. class A { A( const B& ); } // A 實現了non-explicit的參數為B(可以有其他帶默
認值的參數)構造函數
d. A& operator= ( const A& ); // 賦值操作,雖不是正宗的隱式類型轉換,但也可以勉強算一個以下代碼有什么問題?
struct Test
{
Test( int ) {}
Test() {}
void fun() {}
};
void main( void )
{
Test a(1);
a.fun();
Test b();
b.fun();
}
答:變量b定義出錯。按默認構造函數定義對象,不需要加括號。
- 以下代碼有什么問題?
cout << (true?1:"1") << endl;
答:三元表達式“?:”問號后面的兩個操作數必須為同一類型。
- 以下代碼能夠編譯通過嗎,為什么?
unsigned int const size1 = 2;
char str1[ size1 ];
unsigned int temp = 0;
cin >> temp;
unsigned int const size2 = temp;
char str2[ size2 ];
答:str2定義出錯,size2非編譯器期間常量,而數組定義要求長度必須為編譯期常量。
- 以下代碼中的輸出語句輸出0嗎,為什么?
struct CLS
{
int m_i;
CLS( int i ) : m_i(i) {}
CLS()
{
CLS(0);
}
};
CLS obj;
cout << obj.m_i << endl;
答:不能。在默認構造函數內部再調用帶參的構造函數屬用戶行為而非編譯器行為,亦即僅
執行函數調用,而不會執行其后的初始化表達式。只有在生成對象時,初始化表達式才會隨
相應的構造函數一起調用。
- C++中的空類,默認產生哪些類成員函數?
答:
class Empty
{
public:
Empty(); // 缺省構造函數
Empty( const Empty& ); // 拷貝構造函數
~Empty(); // 析構函數
Empty& operator=( const Empty& ); // 賦值運算符
Empty* operator&(); // 取址運算符
const Empty* operator&() const; // 取址運算符 const
};
- 以下兩條輸出語句分別輸出什么?
float a = 1.0f;
cout << (int)a << endl;
cout << (int&)a << endl;
cout << boolalpha << ( (int)a == (int&)a ) << endl; // 輸出什么?
float b = 0.0f;
cout << (int)b << endl;
cout << (int&)b << endl;
cout << boolalpha << ( (int)b == (int&)b ) << endl; // 輸出什么?
答:分別輸出false和true。注意轉換的應用。(int)a實際上是以浮點數a為參數構造了一個整型數,該整數的值是1,(int&)a則是告訴編譯器將a當作整數看(并沒有做任何實質上的轉換)。因為1以整數形式存放和以浮點形式存放其內存數據是不一樣的,因此兩者不等。對b的兩種轉換意義同上,但是0的整數形式和浮點形式其內存數據是一樣的,因此在這種特殊情形下,兩者相等(僅僅在數值意義上)。注意,程序的輸出會顯示(int&)a=1065353216,這個值是怎么來的呢?前面已經說了,1以浮點數形式存放在內存中,按ieee754規定,其內容為0x0000803F(已考慮字節反序)。這也就是a這個變量所占據的內存單元的值。當(int&)a出現時,它相當于告訴它的上下文:“把這塊地址當做整數看待!不要管它原來是什么。”這樣,內容0x0000803F按整數解釋,其值正好就是1065353216(十進制數)。通過查看匯編代碼可以證實“(int)a相當于重新構造了一個值等于a的整型數”之說,而(int&)的作用則僅僅是表達了一個類型信息,意義在于為cout<<及==選擇正確的重載版。
- 請簡述以下兩個for循環的優缺點
for (i=0; i<N; i++)
{
if (condition)
DoSomething();
else
DoOtherthing();
}
if (condition)
{
for (i=0; i<N; i++)
DoSomething();
}
else
{
for (i=0; i<N; i++)
DoOtherthing();
}
優點:程序簡潔
缺點:多執行了N-1次邏輯判斷,并且打斷了循環“流水線”作業,使得編譯器不能對循環進行優化處理,降低了效率。
優點:循環的效率高
缺點:程序不簡潔
- 下列代碼
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
請問運行Test函數會有什么樣的結果?
答:程序崩潰。
因為GetMemory并不能傳遞動態內存,
Test函數中的 str一直都是 NULL。
strcpy(str, "hello world");將使程序崩潰。
- 下列代碼
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
請問運行Test函數會有什么樣的結果?
答:可能是亂碼。
因為GetMemory返回的是指向“棧內存”的指針,該指針的地址不是 NULL,但其原
現的內容已經被清除,新內容不可知。
- 下列代碼
void GetMemory2(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
請問運行Test函數會有什么樣的結果?
答:
(1)能夠輸出hello
(2)內存泄漏
- 下列代碼
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL)
{
strcpy(str, “world”);
printf(str);
}
}
請問運行Test函數會有什么樣的結果?
答:篡改動態內存區的內容,后果難以預料,非常危險。因為free(str);之后,str成為野指針,if(str != NULL)語句不起作用。
- 請閱讀以下一段程序,并給出答案。
class A
{
public:
A(){ doSth(); }
virtual void doSth(){printf("I am A");}
};
class B:public A
{
public:
virtual void doSth(){ printf("I am B");}
};
B b;
執行結果是什么?為什么?
答:執行結果是I am A
因為b對象構造時調用基類A的構造函數A(),得此結果。
- 實現雙向鏈表刪除一個節點P,在節點P后插入一個節點,寫出這兩個函數;
答:雙向鏈表刪除一個節點P
template<class type> void list<type>::delnode(int p)
{
int k=1;
listnode<type> *ptr,*t;
ptr=first;
while(ptr->next!=NULL&&k!=p)
{
ptr=ptr->next;
k++;
}
t=ptr->next;
cout<<"你已經將數據項 "<<t->data<<"刪除"<<endl;
ptr->next=ptr->next->next;
length--;
delete t;
}
在節點P后插入一個節點:
template<class type> bool list<type>::insert(type t,int p)
{
listnode<type> *ptr;
ptr=first;
int k=1;
while(ptr!=NULL&&k<p)
{
ptr=ptr->next;
k++;
}
if(ptr==NULL&&k!=p)
return false;
else
{
listnode<type> *tp;
tp=new listnode<type>;
tp->data=t;
tp->next=ptr->next;
ptr->next=tp;
length++;
return true;
}
}
-
完成下列程序
image.png
#include<iostream>
using namespace std;
const int n = 8;
int main()
{
int i;
int j;
int k;
for(i = n; i >= 1; i--)
{
for(j = 0; j < n-i+1; j++)
{
cout<<"*";
for(k=1; k < n-i+1; k++)
{
cout<<".";
}
}
cout<<endl;
}
return 0;
}
- 完成程序,實現對數組的降序排序
#include <iostream>
using namespace std;
void sort(int* arr, int n);
int main()
{
int array[]={45,56,76,234,1,34,23,2,3};
sort(array, 9);
for(int i = 0; i <= 8; i++)//曾經在這兒出界
cout<<array[i]<<" ";
cout<<endl;
}
void sort(int* arr, int n)
{
int temp;
for(int i = 1; i < 9; i++)
{
for(int k = 0; k < 9 - i; k++)//曾經在這兒出界
{
if(arr[k] < arr[k + 1])
{
temp = arr[k];
arr[k] = arr[k + 1];
arr[k + 1] = temp;
}
}
}
}
- 以下兩條輸出語句分別輸出什么?
#include <iostream>
using namespace std;
int main()
{
float a = 1.0f;
cout << (int)a << endl;
cout << (int&)a << endl;
cout << "boolalpha" << ( (int)a == (int&)a ) << endl; // 輸出什么?
float b = 0.0f;
cout << (int)b << endl;
cout << (int&)b << endl;
cout << "boolalpha" << ( (int)b == (int&)b ) << endl; // 輸出什么?
}
- 寫一個函數,完成內存之間的拷貝。
答:
void* mymemcpy( void *dest, const void *src, size_t count )
{
char* pdest = static_cast<char*>( dest );
const char* psrc = static_cast<const char*>( src );
if( pdest>psrc && pdest<psrc+cout ) 能考慮到這種情況就行了
{
for( size_t i=count-1; i!=-1; --i )
pdest[i] = psrc[i];
}
else
{
for( size_t i=0; i<count; ++i )
pdest[i] = psrc[i];
}
return dest;
}
int main( void )
{
char str[] = "0123456789";
mymemcpy( str+1, str+0, 9 );
cout << str << endl;
return 0;
}
對于C++中類(class) 與結構(struct)的描述正確的為:
A,類中的成員默認是private的,當是可以聲明為public,private 和protected,
結構中定義的成員默認的都是public;
B,結構中不允許定義成員函數,當是類中可以定義成員函數;
C,結構實例使用malloc() 動態創建,類對象使用new 操作符動態分配內存;
D,結構和類對象都必須使用new 創建;
E,結構中不可以定義虛函數,當是類中可以定義虛函數.
F,結構不可以存在繼承關系,當是類可以存在繼承關系.
答:A,D,F兩個互相獨立的類:ClassA 和 ClassB,都各自定義了非靜態的公有成員函數
PublicFunc() 和非靜態的私有成員函數 PrivateFunc();
現在要在ClassA 中增加定義一個成員函數ClassA::AdditionalPunction(ClassA a,ClassB b);則可以在AdditionalPunction(ClassA x,ClassB y)的實現部分(函數功能體內部)出現的合法的表達是最全的是:
A,x.PrivateFunc();x.PublicFunc();y.PrivateFunc();y.PublicFunc();
B,x.PrivateFunc();x.PublicFunc();y.PublicFunc();
C,x.PrivateFunc();y.PrivateFunc();y.PublicFunc();
D,x.PublicFunc();y.PublicFunc();
答:BC++程序下列說法正確的有:
A,對調用的虛函數和模板類都進行遲后編譯.
B,基類與子類中函數如果要構成虛函數,除了要求在基類中用virtual 聲名,而且必須名字
相同且參數類型相同返回類型相同
C,重載的類成員函數都必須要:或者返回類型不同,或者參數數目不同,或者參數序列的類
型不同.
D,靜態成員函數和內聯函數不能是虛函數,友員函數和構造函數也不能是虛函數,但是析構
函數可以是虛函數.
答:A頭文件的作用是什么?
答:
一、通過頭文件來調用庫功能。在很多場合,源代碼不便(或不準)向用戶公布,只要
向用戶提供頭文件和二進制的庫即可。用戶只需要按照頭文件中的接口聲明來調用庫功能,
而不必關心接口怎么實現的。編譯器會從庫中提取相應的代碼。
二、頭文件能加強類型安全檢查。如果某個接口被實現或被使用時,其方式與頭文件中的聲
明不一致,編譯器就會指出錯誤,這一簡單的規則能大大減輕程序員調試、改錯的負擔。寫出下列程序所有可能的運行結果。
int a;
int b;
int c;
void F1()
{
b=a*2;
a=b;
}
void F2()
{
c=a+1;
a=c;
}
main()
{
a=5;
//Start F1,F2 in parallel
F1(); F2();
printf("a=%d\n",a);
}
答:輸出a=11。
一個鏈表的操作,注意代碼的健壯和安全性。要求:
(1)增加一個元素;
(2)獲得頭元素;
(3)彈出頭元素(獲得值并刪除)。下列代碼
unsigned short array[]={1,2,3,4,5,6,7};
int i = 3;
*(array + i) = ?
答:
4
- 下列代碼
class A
{
virtual void func1();
void func2();
}
Class B: class A
{
void func1(){cout << "fun1 in class B" << endl;}
virtual void func2(){cout << "fun2 in class B" << endl;}
}
A, A中的func1和B中的func2都是虛函數.
B, A中的func1和B中的func2都不是虛函數.
C, A中的func2是虛函數.,B中的func1不是虛函數.
D, A中的func2不是虛函數,B中的func1是虛函數.
答:
A
- 輸出下面程序結果。
#include <iostream>
using namespace std;
class A
{
public:
virtual void print(void)
{
cout<<"A::print()"<<endl;
}
};
class B:public A
{
public:
virtual void print(void)
{
cout<<"B::print()"<<endl;
};
};
class C:public B
{
public:
virtual void print(void)
{
cout<<"C::print()"<<endl;
}
};
void print(A a)
{
a.print();
}
int main(void)
{
A a, *pa,*pb,*pc;
B b;
C c;
pa=&a;
pb=&b;
pc=&c;
a.print();
b.print();
c.print();
pa->print();
pb->print();
pc->print();
print(a);
print(b);
print(c);
}
- 程序改錯
class mml
{
private:
static unsigned int x;
public:
mml(){ x++; }
mml(static unsigned int &) {x++;}
~mml{x--;}
pulic:
virtual mon() {} = 0;
static unsigned int mmc(){return x;}
......
};
class nnl:public mml
{
private:
static unsigned int y;
public:
nnl(){ x++; }
nnl(static unsigned int &) {x++;}
~nnl{x--;}
public:
virtual mon() {};
static unsigned int nnc(){return y;}
......
};
代碼片斷:
mml* pp = new nnl;
..........
delete pp;
答:
基類的析構函數應該為虛函數
virtual ~mml{x--;}
- 101個硬幣100真、1假,真假區別在于重量。請用無砝碼天平稱兩次給出真幣重還是假幣重的結論。
答:
101個先取出2堆,
33,33
第一次稱,如果不相等,說明有一堆重或輕
那么把重的那堆拿下來,再放另外35個中的33,如果相等,說明假的重,如果不相等,新放上去的還是重的話,說明假的輕(不可能新放上去的輕)
第一次稱,如果相等的話,這66個肯定都是真的,從這66個中取出35個來,與剩下的沒稱過的35個比
下面就不用說了
方法二:
可以拿A(50),B(50)比一下,一樣的話拿剩下的一個和真的比一下。如果不一樣,就拿其中的一堆。比如A(50)再分成兩堆25比一下,一樣的話就在B(50)中,不一樣就在A(50)中,結合第一次的結果就知道了。
- 寫出程序結果:
void Func(char str[100])
{
printf("%d\n", sizeof(str));
}
答:
4
分析:
指針長度
- 代碼語句
int id[sizeof(unsigned long)];
這個對嗎?為什么??
答:
對
這個 sizeof是編譯時運算符,編譯時就確定了。可以看成和機器有關的常量。
- sizeof應用在結構上的情況
請看下面的結構:
struct MyStruct
{
double dda1;
char dda;
int type
};
對結構MyStruct采用sizeof會出現什么結果呢?sizeof(MyStruct)為多少呢?也許你會這樣求:
sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13
但是當在VC中測試上面結構的大小時,你會發現sizeof(MyStruct)為16。你知道為什么在VC中會得出這樣一個結果嗎?其實,這是VC對變量存儲的一個特殊處理。為了提高CPU的存儲速度,VC對一些變量的起始地址做了"對齊"處理。在默認情況下,VC規定各成員變量存放的起始地址相對于結構的起始地址的偏移量必須為該變量的類型所占用的字節數的倍數。
下面列出常用類型的對齊方式(vc6.0,32位系統)。
類型 對齊方式(變量存放的起始地址相對于結構的起始地址的偏移量)
Char 偏移量必須為sizeof(char)即1的倍數
int 偏移量必須為sizeof(int)即4的倍數
float 偏移量必須為sizeof(float)即4的倍數
double 偏移量必須為sizeof(double)即8的倍數
Short 偏移量必須為sizeof(short)即2的倍數
各成員變量在存放的時候根據在結構中出現的順序依次申請空間,同時按照上面的對齊方式調整位置,空缺的字節VC會自動填充。同時VC為了確保結構的大小為結構的字節邊界數(即該結構中占用最大空間的類型所占用的字節數)的倍?
- 下列代碼
#include "stdafx.h"
#define SQR(X) X*X
int main(int argc, char* argv[])
{
int a = 10;
int k = 2;
int m = 1;
a /= SQR(k+m)/SQR(k+m);
printf("%d\n",a);
return 0;
}
這道題目的結果是什么啊?
define 只是定義而已,在編擇時只是簡單代換X*X而已,并不經過算術法則的
a /= k+m*k+m/k+m*k+m;
=>a /= (k+m)*1*(k+m);
=>a = a/9;
=>a = 1;
- 下面的代碼有什么問題?
void DoSomeThing(...)
{
char* p;
p = malloc(1024); // 分配1K的空間
if (NULL == p)
return;
p = realloc(p, 2048); // 空間不夠,重新分配到2K
if (NULL == p)
return;
}
p = malloc(1024); 應該寫成: p = (char *) malloc(1024);
沒有釋放p的空間,造成內存泄漏。
- 下面的代碼有什么問題?并請給出正確的寫法。
void DoSomeThing(char* p)
{
char str[16];
int n;
assert(NULL != p);
sscanf(p, "%s%d", str, n);
if (0 == strcmp(str, "something"))
{
}
}
sscanf(p, "%s%d", str, n); 這句該寫成: sscanf(p, "%s%d", str, &n);
- 下面代碼有什么錯誤?
void test1()
{
char string[10];
char *str1="0123456789";
strcpy(string, str1);
}
數組越界
- 下面代碼有什么問題?
void test2()
{
char string[10], str1[10];
for(i=0; i<10;i++)
{
str1[i] ='a';
}
strcpy(string, str1);
}
數組越界
- 下面代碼有什么問題?
void test3(char* str1)
{
char string[10];
if(strlen(str1)<=10)
{
strcpy(string, str1);
}
}
數組越界
strcpy拷貝的結束標志是查找字符串中的\0 因此如果字符串中沒有遇到\0的話 會一直復制,直到遇到\0,上面的123都因此產生越界的情況建議使用 strncpy 和 memcpy
- 寫出運行結果:
{
char str[] = "world"; cout << sizeof(str) << ": ";
char *p = str; cout << sizeof(p) << ": ";
char i = 10; cout << sizeof(i) << ": ";
void *pp = malloc(10); cout << sizeof(p) << endl;
}
6:4:1:4
C和C++有什么不同?
從機制上:c是面向過程的(但c也可以編寫面向對象的程序);c++是面向對象的,提供了類。但是,c++編寫面向對象的程序比c容易從適用的方向:c適合要求代碼體積小的,效率高的場合,如嵌入式;c++適合更上層的,復雜的; llinux核心大部分是c寫的,因為它是系統軟件,效率要求極高。從名稱上也可以看出,c++比c多了+,說明c++是c的超集;那為什么不叫c+而叫c++呢,是因為c++比c來說擴充的東西太多了,所以就在c后面放上兩個+;于是就成了c++C語言是結構化編程語言,C++是面向對象編程語言。C++側重于對象而不是過程,側重于類的設計而不是邏輯的設計。在不用第三方參數的情況下,交換兩個參數的值
#include <stdio.h>
void main()
{
int i=60;
int j=50;
i=i+j;
j=i-j;
)
i=i-j;
printf("i=%d\n",i);
printf("j=%d\n",j);
}
方法二:
i^=j;
j^=i;
i^=j;
方法三:
// 用加減實現,而且不會溢出
a = a+b-(b=a)
- 下面的函數實現在一個固定的數上加上一個數,有什么錯誤,改正
int add_n(int n)
{
static int i=100;
i+=n;
return i;
}
答:
因為static使得i的值會保留上次的值。去掉static就可了。
- 下面代碼
#include <iostream>
#include <stdio.h>
using namespace std;
union a {
int a_int1;
double a_double;
int a_int2;
};
typedef struct{
a a1;
char y;
} b;
class c
{
double c_double;
b b1;
a a2;
};
int main(){
cout << sizeof(a) << endl;
cout << sizeof(b) << endl;
cout << sizeof(c) << endl;
}
輸出cout<<sizeof(c)<<endl;的結果?
答:
Qt下結果,不同編譯器結果不同
- 下面代碼
unsigned short array[]={1,2,3,4,5,6,7};
int i = 3;
*(array + i) = ?
答:4
- 下面代碼
class A
{
virtual void func1();
void func2();
}
Class B: class A
{
void func1(){cout << "fun1 in class B" << endl;}
virtual void func2(){cout << "fun2 in class B" << endl;}
}
A, A中的func1和B中的func2都是虛函數.
B, A中的func1和B中的func2都不是虛函數.
C, A中的func2是虛函數.,B中的func1不是虛函數.
D, A中的func2不是虛函數,B中的func1是虛函數.
答:
A
- 動態連接庫的兩種方式?
答:調用一個DLL中的函數有兩種方法:
1.載入時動態鏈接(load-time dynamic linking),模塊非常明確調用某個導出函數,使得他們就像本地函數一樣。這需要鏈接時鏈接那些函數所在DLL的導入庫,導入庫向系統提供了載入DLL時所需的信息及DLL函數定位。
2.運行時動態鏈接(run-time dynamic linking),運行時可以通過LoadLibrary或LoadLibraryEx 函數載入DLL。DLL 載入后,模塊可以通過調用GetProcAddress 獲取DLL函數的出口地址,然后就可以通過返回的函數指針調用DLL函數了。如此即可避免導入庫文件了。
213.以下反向遍歷array數組的方法有什么錯誤?
vector array;
array.push_back( 1 );
array.push_back( 2 );
array.push_back( 3 );
for( vector::size_type i=array.size()-1; i>=0; --i ) // 反向遍歷array數組
{
cout << array[i] << endl;
}
答:首先數組定義有誤,應加上類型參數:vector<int> array。其次vector::size_type被定義為unsigned int,即無符號數,這樣做為循環變量的i為0時再減1就會變成最大的整數,導致循環失去控制。
- 以下代碼中的輸出語句輸出0嗎,為什么?
struct CLS
{
int m_i;
CLS( int i ) : m_i(i) {}
CLS()
{
CLS(0);
}
};
CLS obj;
cout << obj.m_i << endl;
答:不能。在默認構造函數內部再調用帶參的構造函數屬用戶行為而非編譯器行為,亦即僅
執行函數調用,而不會執行其后的初始化表達式。只有在生成對象時,初始化表達式才會隨
相應的構造函數一起調用。