這是我在準備C++考試時整理的提綱,若是是經過搜索引擎搜索到這篇博客的師弟師妹,建議仍是先參照PPT和課本,這個大綱也不是很準確,本身總結會更有收穫,多去理解含義,不要死記硬背,不然遇到概念辨析題會特別吃虧,若是以爲有收穫點贊關注,祝考試順利。ios
1.敘述面向對象編程的特色是什麼?(提示:封裝、繼承、多態。)程序員
對比面向過程具備抽象、封裝、繼承和多態的特色。算法
封裝是將抽象獲得的數據和行爲相結合,造成了一個有機總體,使得一部分紅員充當類與外部的接口,而將其餘成員隱藏了起來達到了對成員訪問權限的合理控制,使得不一樣類之間的影響最小,增強數據安全,簡化編程。express
繼承容許在保持原有類特性的基礎上,進行更具體、更詳細的說明,可以很好反映出特殊概念和通常概念之間的關係,是代碼複用的基礎機制。編程
多態使得一段程序可以具備處理多種類型對象的能力,相同的消息在不一樣的對象下會有不一樣的動做,加強了編程的靈活性。數組
2.使用const定義常量與用使用define定義常量相比,有什麼優勢?安全
a. const常量有數據類型,而宏常量沒有數據類型。編譯器能夠對const常量進行類型安全檢查,而對宏常量只能字符替換數據結構
b. 有些集成化的調試工具能對const常量進行調試,對宏常量不能調試 ide
c.const定義的常量在程序運行的過程當中只有一份拷貝,而define定義的常量在內存中有若干拷貝。函數
3.用代碼說明在標準C++中如何進行輸入輸出,並解釋各語句的含義是什麼?
cout<<"hello!"<<"world"; cin>>a>>b;
在輸入時,從鍵盤輸入的數據先放在鍵盤緩衝區中,當按回車鍵時,鍵盤緩衝區中的數據輸入到程序中的輸入緩衝區,造成cin流,而後用流提取運算符「>>」從輸入緩衝區中提取數據送給程序中的有關變量。
當用cout和流插入運算符「<<」向顯示器輸出數據時,先將這些數據送到程序中的輸出緩衝區保存,直到緩衝區滿了或遇到endl,就將緩衝區中的所有數據送到顯示器顯示出來。
4.C++中如何進行靜態類型轉換,解釋並舉例說明。
(1)用於類層次結構中基類和派生類之間指針或引用的轉換。進行上行轉換(把派生類的指針或引用轉換成基類表示)是安全的;進行下行轉換(把基類指針或引用轉換成派生類表示)時,因爲沒有動態類型檢查,因此是不安全的。
class Base { }; class Derived:public Base {}; int main() { Derived D; Base* B = static_cast<Base*> (&D); return 0; }
將派生類型的指針轉化爲基類型的指針
(2)用於基本數據類型之間的轉換,如把int轉換成char,把int轉換成enum。這種轉換的安全性也要開發人員來保證。
double a; int b=100; a = static_cast<double> (b);
(3)把空指針轉換成目標類型的空指針(不安全!!)。
(4)把任何類型的表達式轉換成void類型。
5.闡述C++中函數三種調用的方式實現機制、特色及其實參、形參的格式,最好用代碼說明。(提示:傳址、傳值、引用傳遞)
在C++中調用函數時有三種參數傳遞方式:
(1)傳值調用;
int main( ) { void swap(int,int); //函數聲明 int i=3,j=5; swap(i,j); //調用函數swap return 0; } void swap(int a,int b) //企圖經過形參a和b的值互換,實現實參i和j的值互換 { int temp; temp=a; //如下3行用來實現a和b的值互換 a=b; b=temp; }
(2)傳址調用(傳指針);
用指針類型做爲形參的值調用方式,能夠經過參數返回修改後的值。
void main( ) { void swap(int *,int *); int i=3,j=5; swap(&i,&j); //實參是變量的地址 } void swap(int *p1,int *p2) //形參是指針變量 { int temp; temp=*p1; //如下3行用來實現i和j的值互換 *p1=*p2; *p2=temp; }
(3)引用傳遞;
按引用傳遞,引用實參的引用參數傳遞給函數,而不是進行參數拷貝。引用類型的形參與相應的實參佔用相同的內存空間,改變引用類型形參的值,相應實參的值也會隨着變化。
int main( ) { void swap(int &,int &); int i=3,j=5; swap(i,j); return 0; } void swap(int &a,int &b) //形參是引用類型 { int temp; temp=a; a=b; b=temp; }
6.什麼是內聯函數?爲何要使用內聯函數?
在編譯時將所調用函數的代碼直接嵌入到主調函數中,而不是將流程轉出去,這種嵌入到主調函數中的函數成爲內聯函數。
爲了節省參數傳遞、控制轉換等開銷,好比:壓棧、彈棧、保存現場與恢復現場。
7.什麼是類的前向聲明?使用類的前向聲明時,要注意什麼?
遇到倆個類相互引用的循環依賴狀況
class B; //前向引用聲明 class A//A類的定義 { public://外部接口 void f(B b);//以B類對象b爲形參的成員函數 }; class B//B類的定義 { public://外部接口 void g(A a);//以A類對象a爲形參的成員函數 };
前向引用聲明,是在引用未定義的類以前,聲明該類,使編譯器知道那是一個類名。這樣,當程序中使用這個類名時,編譯器就不會認爲是錯誤,而類的完整定義能夠在程序的其餘地方。
注意:儘管使用了前向引用聲明,可是在提供一個完整的類聲明以前,不能定義該類的對象,也不能在成員函數中使用該類的對象。只能用於定義指針、引用、以及用於函數形參的指針和引用。當你使用前向引用聲明時,你只能使用被聲明的符號,而不能涉及類的任何細節。
8.什麼是先驗條件(Precondition),什麼是後置條件(Postcondition)?(google)
先演條件是在執行某段代碼或正式規範操做以前必須始終爲真的條件或謂詞。好比輸入一個時間必須小於24。
後置條件是在執行某段代碼或正式規範操做以後必須始終爲真的條件或謂詞。好比計算輸入數字的平方根程序可能具備結果爲數字的後置條件,且其平方等於輸入。
9.什麼是名稱空間(namespace)?它的主要做用是什麼?要使用一個名稱空間中聲明的標識符,方式有哪些?
A namespace is a scope.
C++ provides namespaces to prevent name conflicts
名字空間實質上是一種做用域。名字空間是一種描述邏輯分組的機制,是爲了解決C++中的變量、函數命名衝突而服務的。
C++標準程序庫中的全部標識符都被定義於一個名爲std的namespace中。
因爲namespace的概念,使用C++標準程序庫的任何標識符時,能夠有三種選擇:
a、直接指定標識符。例如std::ostream而不是ostream。完整語句以下:
#include <iostream> std::cout << "hello!!"<< std::endl;
b、使用using關鍵字進行聲明
顯然,當某個名字在它本身的名字空間以外頻繁使用時,在反覆寫它時都要加上名字空間來做限定詞,是一件使人厭煩的事情。這時,能夠經過一個使用聲明而清楚掉,只須要在某個地方說明,在這個做用域其後的代碼中,使用此名字時能夠自動解析出此名字所在的空間。例如:
#include <iostream> using std::cout; using std::endl;
此後在使用cout和endl時,都無需再加上名字空間前緣了:
cout <<"hello!!"<< endl;
c、最方便的就是使用指令using namespace std;
一個使用指令能把來自另外一個名字空間的全部名字都變成在當前名字空間內可用,就像這些名字就在當前名字空間中同樣。
例如,在一個名字空間內有命令 using namespace std; 則在此後的代碼中(當前空間最小局部內),對於全部名字都無需有名字空間前綴便可使用。
#include <iostream> using namespace std;
10.什麼是重載(Overloading),解釋並舉例說明?可否根據返回值不一樣,對函數進行重載,爲何?
C++有兩種重載:函數重載和運算符重載。
C++容許用同一函數名定義多個函數,這些函數的參數個數和參數類型不一樣。這就是函數的重載(function overloading)。
運算符重載實質上是函數的重載,運算符重載經過運算符函數實現。
int max(int a,int b, int c); double max(double a,double b,double c); long max(long a,long b,long c);
不能根據返回值不一樣進行重載。
由於調用時不能指定類型信息,編譯器不知道你要調用哪一個函數。
例如
float max(int a, int b); int max(int a, int b);
當調用max(1, 2);時沒法肯定調用的是哪一個,單從這一點上來講,僅返回值類型不一樣的重載是不該該容許的。
11.關鍵字const的用法有哪些?(google)
1、定義常量
常量不可修改
const int val = 5; int const val = 5;
與#define宏定義常量的區別:
(1)const常量具備類型,編譯器能夠進行安全檢查;#define宏定義沒有數據類型,只是簡單的字符串替換,不能進行安全檢查。
(2)有些集成化的調試工具能對const常量進行調試,對宏常量不能調試
2、修飾指針
(1)const int* p; //指針p指向的內容是常量,不可改變。
(2)int* const p; //指針自己是一個常量,不可改變。
(3)const int* const p; //指針自己和指向的內容都是常量,都不能夠改變。
區分方法,*p表明對象內容,p表明指針自己,看const修飾的是哪一個。
3、在函數中使用const
修飾函數參數
void function(const int Var);
代表參數在函數體內不能被修改,但此處沒有任何意義,Var自己就是形參,在函數內不會改變。
包括傳入的形參是指針也是同樣。
(1)使用引用參數,能夠防止建立副本,減小內存開銷,同時能夠在函數中對引用參數修改,函數結束後,引用參數的修改仍然存在。
(2)若是爲了防止對引用參數進行修改,能夠對該參數加上const關鍵字。
修飾函數返回值
與修飾普通變量和指針意義差很少,而在傳引用時,若是不但願函數返回值被改變,就能夠添加關鍵字 const 。
4、在類中使用const
修飾類成員變量
class A { const int nValue; }
(1)成員常量不可被修改。
(2)只能在初始化列表中被賦值。
修飾類成員函數
class A { void function()const; }
(1)常成員函數, 它不改變對象的成員變量. 表明只讀函數,增長程序的可讀性。
(2)不能調用類中任何非const成員函數。
12.操做符new的做用是什麼?如何申請單個空間?如何申請動態數組?用new建立一個類的對象時,會發生哪些操做?必要時,請用代碼說明。
做用:在堆中申請一段空間,動態分配內存
申請單個空間int *i = new int;
申請動態數組int *a = new int[10];
new建立類對象須要指針接收,一處初始化,多處使用,做用域是全局,且須要手動釋放空間,在堆中動態分配內存,調用構造函數。
13.操做符delete的做用是什麼?如何刪除單個用new申請的空間?如何刪除申請的動態數組?用delete刪除一個類的對象時,會發生哪些操做?必要時,請用代碼說明。
做用:釋放所申請的空間
釋放單個空間delete i;
釋放動態數組delete []a;
釋放在堆中分配的內存,調用析構函數。
14.什麼是懸掛指針(又稱爲野指針,Dangling Pointers),其危害是什麼?(google)
指針指向非法的內存地址,那麼這個指針就是懸掛指針,也叫野指針。意爲沒法正常使用的指針。野指針形成的危害程度和危害時間未知,由於野指針指向的內存空間,有多是某個重要的數據或其餘程序。嚴重的狀況下會形成程序崩潰。
15.什麼是類?一般一個類中,包含什麼樣的內容?定義一個類的語法是什麼,試舉例說明。
類是邏輯上相關的函數與數據的封裝,描述了所建立對象共同的屬性和方法。類中聲明或定義的變量和函數稱爲成員,類的成員包括數據成員和函數成員,數據成員描述問題的屬性,函數成員描述問題的行爲。
16.什麼是對象?什麼是類?類與對象的關係是什麼?
類是邏輯上相關的函數與數據的封裝,它是對問題的抽象描述。
對象是類的某一特定實體。
將整個公司的僱員當作一個類,那麼每個僱員就是該類的一個特定實體,也就是一個對象。
類對象的關係:類是對象的抽象,而對象是類的具體實例。類是抽象的,不佔用內存,而對象是具體的,佔用存儲空間。類是用於建立對象的藍圖,它是一個定義包括在特定類型的對象中的方法和變量的軟件模板。
17.類中的成員能夠用public/protected/private分別進行修飾,這三種成員在什麼狀況下是能夠被訪問的?類中沒有用public/protected/private修飾的成員,其可訪問性是什麼,結構體中沒有用public/protected/private修飾的成員,其可訪問性是什麼?
public修飾的成員能夠在任何地方被訪問
private修飾的成員只能由該類中的函數、其友元函數訪問;不能被任何其餘訪問,該類對象也不能訪問。
protected修飾的成員能夠被該類中函數、子類函數、友元函數訪問;但不能被該類對象訪問。
public能夠被訪問,沒有修飾,類的默認爲private,struct默認爲public。
18.什麼是封裝?其做用是什麼?(google)
封裝就是將抽象獲得的數據和行爲(或功能)相結合,造成一個有機的總體,也就是將數據與操做數據的函數代碼進行有機結合,造成類。
做用:
使一部分紅員充當類與外部的接口,而將其餘成員隱藏起來,這樣就達到了對成員訪問權限的合理控制,使不一樣類之間的相互影響減小到最低限度,進而保護數據加強數據的安全性和簡化程序編寫工做。
19.什麼是構造函數?構造函數有返回值嗎?構造函數如何命名?構造函數能夠重載嗎?什麼是缺省構造函數(default constructor)?什麼狀況下,類中會有缺省構造函數?
構造函數主要用來在建立對象時初始化對象, 即爲對象成員變量賦初始值。
構造函數沒有返回值。
構造函數是一個與其所在的類同名的函數。
構造函數能夠重載。可是, 每一個構造函數必須有不一樣的函數簽名。
若是構造函數沒有參數,或者構造函數的全部參數都有默認值,就能夠稱其爲缺省構造函數。一個類中,只能有一個缺省構造函數。
當沒有定義構造函數或者定義的構造函數沒有參數時,類中會有缺省構造函數。
20.若父類中沒有缺省構造函數,則對派生類的構造函數有什麼要求?
若是父類是一個完好省參數的構造函數,那麼對於派生類一旦沒有構造函數,那麼就不會自動的先構造父類的構造函數,這是不容許的。
派生類中必定要有構造函數。
BaseballTeam(const string s[], int si) : Team(si)
派生類的構造函數經過初始化列表,對基類進行初始化。
21.構造函數的做用是什麼?何時會被調用?構造函數的執行順序是什麼(父類與子類的構造函數、類自身與其數據成員的構造函數)?
構造函數主要用來在建立對象時初始化對象, 即爲對象成員變量賦初始值。
當類被建立時,自動調用。
執行構造函數的順序:
1. 父類的構造函數
2. 數據成員的初始化(成員中有類,執行該類的構造函數)
2. 子類的構造函數
22.什麼是類做用域(Class scope)、文件做用域(file scope)、函數做用域(function scope)?
類做用域:
類是有名成員的集合,類X的成員m具備類做用域,對成員m的訪問方式有以下三種:
1)若是X的成員函數中沒有聲明同名的局部做用域標識符,那麼能夠直接使用成員m
2)經過表達式x.m或X::m(訪問靜態成員)
3)經過ptr->m,其中ptr爲指向X類的一個對象的指針
文件做用域:
在函數外部聲明的變量只在當前文件範圍內(包括文件內全部定義的函數)可用
在其餘文件不可用。要使變量具備文件做用域,必須在變量的聲明前加static關鍵字。
當多個源文件連接成一個程序時,static能夠避免一個文件中的全局變量與其它文件中的變量同名而發生衝突。
函數做用域:
(1)指在函數定義或者複合語句中,從標識符的定義點開始到函數或者一對花括號之間的程序段。
(2)在同一個局部做用域內不能出現相同名字的兩個局部變量(包括形參)。
(3)一個函數內的複合語句又是一個局部做用域,也就是在函數內有某個變量時,複合語句中能夠有另一個同名字的變量。
23.爲何拷貝構造函數(copy constructor)的參數必須是按引用傳遞(by reference)而不能是按值傳遞(by value)?
1.無限遞歸調用:
當一個對象須要以值方式傳遞時編譯器會生成代碼調用它的拷貝構造函數以生成一個複本。若是類A的拷貝構造函數是以值方式傳遞一個類A對象做爲參數的話,當須要調用類A的拷貝構造函數時,須要以值方式傳進一個A的對象做爲實參;而以值方式傳遞須要調用類A的拷貝構造函數;結果就是調用類A的拷貝構造函數致使又一次調用類A的拷貝構造函數,這就是一個無限遞歸。
2在某些情況下,類內成員變量須要動態開闢堆內存,若是實行位拷貝,也就是把對象裏的值徹底複製給另外一個對象,如A=B。這時,若是B中有一個成員變量指針已經申請了內存,那A中的那個成員變量也指向同一塊內存。這就出現了問題:當B把內存釋放了(如:析構),這時A內的指針就是野指針了,出現運行錯誤。
24.拷貝構造函數(複製構造函數)的做用是什麼?什麼是淺拷貝?什麼是深拷貝?(google)
複製構造函數由編譯器調用來完成一些基於同一類的其餘對象的構件及初始化。
淺拷貝只是對指針的拷貝,拷貝後兩個指針指向同一個內存空間,深拷貝不但對指針進行拷貝,並且對指針指向的內容進行拷貝,經深拷貝後的指針是指向兩個不一樣地址的指針。
25.全局對象(Global scope objects)的構造函數、析構函數分別是何時被調用的?
自動局部對象(Automatic local objects)的構造函數、析構函數分別是何時被調用的?
靜態局部對象(static local objects)的構造函數、析構函數分別是何時被調用的?
a.全局變量構造函數程序運行前被調用,在main()函數返回後才被中對象才被銷燬,析構函數在程序結束前最後被調用。
b.自動局部變量,當程序執行到對象定義時,調用自動局部對象的構造函數。該對象的析構函數在對象離開範圍時調用(即離開定義對象的塊時)。自動對象的構造函數與析構函數在每次對象進人和離開範圍時調用。
c.靜態局部對象的構造函數只在程序執行首次到達對象定義時調用一次,對應的析構函數在main終止或調用exit函數時調用。
26.什麼是初始化列表(Initialization Sections)?它的做用是什麼?(提示:通常數據成員的初始化、常成員的初始化,對象成員構選函數的選擇、父類構造函數的選等)。
構造函數初始化列表以一個冒號開始,接着是以逗號分隔的數據成員列表,每一個數據成員後面跟一個放在括號中的初始化式。
class A { public: int a; float b; A(): a(0),b(9.9) {} //構造函數初始化列表 };
初始化列表做用:通常數據成員的初始化、常成員的初始化,對象成員構選函數的選擇、父類構造函數的選擇。
27.什麼是純虛函數?什麼是抽象數據類型(ADT)?抽象類的做用是什麼?抽象類是否可實例化?抽象類的什麼樣子類能夠實例化?(google)
純虛函數是沒有函數體的虛函數,它的實現留給該基類的派生類去作,這就是純虛函數的做用。
抽象類是一種特殊的類,它是爲了抽象和設計的目的而創建的,它處於繼承層次結構的較上層。
抽象類不可實例化,只能夠派生。
抽象類派生的子類必須重置基類的純虛函數才能實現實例化。
抽象數據類型是具備相似行爲的特定類型的數據結構的數學模型:或者具備相似語義的一種或者多種程序設計語言的數據類型。
抽象數據類型的描述包括給出抽象數據類型的名稱、數據的集合、數據之間的關係和操做的集合等方面的描述。抽象數據類型的設計者根據這些描述給出操做的具體實現,抽象數據類型的使用者依據這些描述使用抽象數據類型。
抽象數據類型描述的通常形式以下:
ADT 抽象數據類型名稱
{
數據對象:
……
數據關係:
……
操做集合:
操做名1:
……
……
操做名n:
}ADT抽象數據類型名稱
抽象數據類型定義(ADT)
做用:抽象數據類型可使咱們更容易描述現實世界。例:用線性表描述學生成績表,用樹或圖描述遺傳關係。
定義:一個數學模型以及定義在該模型上的一組操做。
關鍵:使用它的人能夠只關心它的邏輯特徵,不須要了解它的存儲方式。定義它的人一樣沒必要要關心它如何存儲。
例:線性表這樣的抽象數據類型,其數學模型是:數據元素的集合,該集合內的元素有這樣的關係:除第一個和最後一個外,每一個元素有惟一的前趨和惟一的後繼。能夠有這樣一些操做:插入一個元素、刪除一個元素等。
28.什麼是this指針,其做用是什麼?
this指針是一個隱含於每個成員函數中的特殊指針。它是一個指向正在被該成員函數操做的對象,也就是要操做該成員函數的對象。經過this指針能夠訪問當前對象的全部成員。
this做用域是在類內部,當對一個對象調用成員函數時,編譯程序先將對象的地址賦給this指針,編譯器會自動將對象自己的地址做爲一個隱含參數傳遞給函數。
在如下場景中,常常須要顯式引用this指針:
(1)在類的非靜態成員函數中返回類對象自己的時候,直接使用 return *this,例如:實現對象的鏈式引用。
(2)當參數與成員變量名相同時,如this->x = x,不能寫成x = x。
(3)避免對同一對象進行賦值操做。
if(&pointer!=this) //同一對象之間的賦值沒有意義,因此要保證pointer不等於this { X=pointer.X; Y=pointer.Y; }
29.什麼是友元(friend)函數?爲何要使用友員函數?
友元函數是在類聲明中由關鍵字friend修飾說明的非成員函數或其它類的成員函數,在它的函數體中可以經過對象名訪問 private 和 protected成員
友元函數能夠訪問這個類中的私有成員,增長靈活性,使程序員能夠在封裝和快速性方面作合理選擇。
友元是C++提供的一種對數據封裝和數據隱藏的破壞機制。
30.如何防止一個頭文件被多重包含?舉例說明。
#include "a.h"
#include "b.h"
若是a.h和b.h都包含了一個頭文件x.h。那麼x.h在此也一樣被包含了兩次,只不過它的形式不是那麼明顯而已。
可使用條件編譯。
#ifndef _HEADERNAME_H #define _HEADERNAME_H ..//(頭文件內容) #endif
當頭文件第一次被包含時,它被正常處理,符號_HEADERNAME_H被定義爲1。若是頭文件被再次包含,經過條件編譯,它的內容被忽略。
符號_HEADERNAME_H按照被包含頭文件的文件名進行取名,以免因爲其餘頭文件使用相同的符號而引發的衝突。可是,你必須記住預處理器仍將整個頭文件讀入,即便這個頭文件全部內容將被忽略。因爲這種處理將託慢編譯速度,因此若是可能,應該避免出現多重包含。
31.什麼是運算符重載?爲何要使用運算符重載?如何進行運算符重載,舉例說明。
運算符重載,就是對已有的運算符從新進行定義,賦予其另外一種功能,以適應不一樣的數據類型。
擴展C++中提供的運算符的適用範圍,以用於類所表示的抽象數據類型。同一個運算符,對不一樣類型的操做數,所發生的行爲不一樣。
運算符重載的函數通常地採用以下兩種形式:成員函數形式和友元函數形式。這兩種形式均可訪問類中的私有成員。
class Complex { public: Complex( ) { real=0; imag=0; } Complex(double r,double i) { real=r; imag=i; } Complex operator+( const Complex & ) const; //重載爲成員函數 friend Complex operator+(Complex &c1,Complex &c2); //重載爲友員函數 void display( ); private: double real; double imag; };
32.爲何重載爲全局函數的運算符一般要比重載爲成員函數的運算符多一個參數?舉例說明。
當重載爲成員函數時,會有一個this指針,指向當前的類,因此只須要一個參數就能夠了。
而當重載爲全局函數時,將沒有隱含的參數this指針,這樣將會多一個參數。
Complex operator +(Complex&); friend Complex operator+(Complex &c1,Complex &c2);
33.什麼是析構函數?析構函數有返回值嗎?析構函數如何命名?析構函數能夠重載嗎?
與構造函數相反,當對象結束其生命週期,如對象所在的函數已調用完畢時,系統會自動執行析構函數。
析構函數沒有返回值。
名字與類名相同,在前面加‘~’。
析構函數不返回任何值,沒有函數類型,也沒有函數參數,所以它不能被重載。
34.析構函數的做用是什麼?何時會被調用?爲何析構函數一般是虛函數,若是不是虛函數,會如何?(google)
析構函數對象消亡時即自動被調用。
做用:清空並釋放對象先前建立或者佔用的內存資源。
若是析構函數不被聲明成虛函數,則編譯器採用的綁定方式是靜態綁定,在刪除基類指針時,只會調用基類析構函數,而不調用派生類析構函數,這樣就會致使基類指針指向的派生類對象析構不徹底。
如果將析構函數聲明爲虛函數,無論派生類的析構函數前是否加virtual(能夠理解爲編譯器優化),都構成重寫。基類的指針指向派生類的對象,而後調用重寫虛函數——析構函數,構成了多態,而多態與類型無關,只與對象有關,因此就可以調用的就是派生類的析構函數了。
35.在一個類中,爲何靜態成員函數(static member function)中不能使用this指針?
靜態成員函數並非針對某個類的實例對象,而是屬於整個類的,爲全部的對象實例所共有。他在做用域的範圍內是全局的,獨立於類的對象以外的。他只對類內部的靜態成員變量作操做。當實例化一個類的對象時候,裏面不存在靜態成員的。this指針是至關於一個類的實例的指針,this是用來操做對象實例的內容的,既然靜態成員函數和變量都是獨立於類的實例對象以外的,他就不能用this指針。也不能操做非靜態成員。
36.若是要編寫一段程序,跟蹤類A所建立的實例的個數,請敘述編寫程序的大致思路。
#include<iostream> #include<string.h> #include<stdio.h> using namespace std; class A { public: A() { i++; } ~A() { i--; } int get() { return i; } private: static int i; }; int A::i(0); int main() { A c; A b; A e; cout<<c.get()<<endl; A *p=new A; cout<<c.get()<<endl; delete p; cout<<c.get()<<endl; return 0; }
37.什麼是C++中的三大函數(The Big Three)?(google)
Big Three: 是指 若是一個類要實現下面某一個成員函數,通常都要一塊兒實現另外兩個:
1)Desconstructor 析構函數
2) copy constructor 拷貝構造函數
3) operator = 賦值函數
38.什麼是UML?如何畫UML中的類圖?什麼是類與類之間依賴關係、關聯關係、包含關係?試舉例說明這三種類之間的關係。這三種關係如何和UML圖表示?
UML統一建模語言,UML語言是一種可視化的的面向對象建模語言,描述軟件模型的靜態結構、動態行爲及模塊組織與管理。
依賴關係:一個事物的變化可能會影響到使用它的另外一個事物。舉例:駕駛員(源)開車(目標)。
關聯關係:一個類的對象和另外一個類的對象之間相互做用。舉例:老師和學生,小明的語文老師是張老師,張老師的學生有小明。
包含關係:彙集和組合
彙集表示類之間的關係是總體與部分的關係。舉例:班級成員和學生。
組合是指總體擁有各個部分,總體和部分共存,總體不存在了,部分也會隨之消失。舉例:打開一個視窗口,它由標題、外框和顯示區域組成,視窗口是一個總體,它消失了,部分也就隨之消失了。
繼承關係:在UML中稱爲泛化。舉例:鴨子和鳥,鴨子是一種鳥,繼承了鳥的特性。
39.常見的類與類之間的關係有哪幾種,舉例說明每種關係的對應UML圖如何畫?兩個什麼樣的類能夠實現爲繼承關係?(google)
依賴關係、關聯關係、包含關係、繼承關係。
具備共同屬性的兩個類能夠實現繼承關係。
40.父類成員中的public、protected、private成員,哪些在子類中是能夠訪問的?
在公有繼承、私有繼承、受保護繼承三種繼承方式下,父類成員中的public、protected、private成員被繼承到子類後,其可訪問性分別是什麼?
派生類是否能夠繼承父類的構造函數和析構函數?
public 和protected是能夠訪問的,private不可訪問。
公有繼承:public、protected、private
私有繼承:private、private、private
保護繼承:protected、protected、private
派生類不能繼承父類的構造函數和析構函數。
41.多重繼承會帶來什麼問題?在C++中是如何解決的?
問題1:類DC的對象中存在多個同名成員 x, 應如何使用?
問題2:類DC的對象中,存在兩份來自類BC0的成員K,如何區分?
解決方案:
在BC1類和BC2類繼承BC0時,其前面加上virtual關鍵字就能夠實現虛擬繼承,使用虛擬繼承後,當系統碰到多重繼承的時候就會先自動加一個BC0的拷貝,當再次請求一個BC0的拷貝時就會被忽略,以保證繼承類成員函數的惟一性。
class BC0 { public: int K; }; class BC1 : virtual public BC0 { public: int x; }; class BC2 : virtual public BC0 { public: int x; }; class DC : public BC1, public BC2 { }; void main( ) { DC d; //虛繼承使得BC0僅被DC間接繼承一份 d.K = 13; // OK }
42.對於函數調用,什麼是前期綁定(Early Binding,又稱爲靜態聯編)?什麼是的後期綁定(Late Binding,又稱爲動態聯編)?重載函數是後期綁定嗎,若是不是爲何?
綁定:程序自身彼此關聯的過程,肯定程序中的操做調用與執行該操做的代碼間的關係。例如把一個標示符名和一個存儲地址聯繫在一塊兒的過程。
用面向對象的術語講,就是把一條消息和一個對象的方法相結合的過程。
按照綁定進行的階段的不一樣,能夠分爲靜態綁定和動態綁定兩種。
靜態綁定:綁定工做在編譯鏈接階段完成。
由於綁定過程是在程序開始執行以前進行的,所以也稱爲早期綁定或前綁定。
在編譯、鏈接過程當中,系統就能夠根據類型匹配等特徵肯定程序中操做調用與執行該操做代碼的關係,即肯定了某一個同名標識究竟是要調用哪一段程序代碼。
動態綁定:和靜態綁定相對應,綁定工做在程序運行階段完成的。
class A { public: virtual void Get( ); }; class B : public A { public: virtual void Get( ); }; void MyFunction( A * pa ) { pa->Get( ); }
pa->Get( ) 調用的是 A::Get( )仍是B::Get( ),編譯時沒法肯定,由於不知道MyFunction被調用時,形參會對應於一個 A 對象仍是B對象。
因此只能等程序運行到 pa->Get( )了,才能決定到底調用哪一個Get( )。
重載函數是靜態綁定。
43.要讓一個函數調用表現出多態特徵,必須知足哪些條件?
a.必須存在繼承關係;
b.子類重寫父類的方法。繼承關係中必須有同名的虛函數,而且它們是覆蓋關係(重載不行)。
c.存在基類的指針,經過該指針調用虛函數。
44.簡述虛函數動態綁定的實現原理。
構造函數中爲對象的虛指針賦值,經過多態類型的指針或引用調用成員函數時,經過虛指針找到虛表,進而找到所調用的虛函數的入口地址,經過該入口地址調用虛函數。
45.什麼是隱藏(hiding)、覆蓋(overriding)、重載(overloading)?對比它們的異同?以C++代碼爲例進行說明。
若基類 B 定義了非虛方法 m,同時其派生類 D 也定義了方法m,此時,咱們說派生類方法 D::m 隱藏了繼承自基類的同名方法 B::m 。因爲函數簽名不一樣,因此兩者不構成重置。故D::m隱藏了B::m。
class B { public: void m(int x) { … } }; class D : public B { public: void m ( )//因爲函數簽名不一樣,因此兩者不構成重置。 { … } }; int main( ) { D d1 ; d1.m(); // invokes D::m() d1.m(10); // ERROR d1.B::m(10); // OK return 0; }
覆蓋(override)是指派生類中存在從新定義的函數,其函數名、參數列、返回值類型必須同父類中的相對應被覆蓋的函數嚴格一致,覆蓋函數和被覆蓋函數只有函數體不一樣,當派生類對象調用派生類中該同名函數時會自動調用派生類中的覆蓋版本,而不是父類中的被覆蓋函數版本,這種機制就叫作覆蓋。
class B { public: virtual void m( ) { … } }; class D : public B { public: void m ()//重置了基類方法,仍然爲虛函數 { } }; int main( ) { B*p= new D; p -> m(); // 動態綁定 D::m() return 0; }
重載:若是頂層函數有不一樣的簽名,則函數名能夠相同。
class B { public: virtual void m( ) { … } }; class D : public B { public: void m ()//重置了基類方法,仍然爲虛函數 { } }; int main( ) { B*p= new D; p -> m(); // 動態綁定 D::m() return 0; }
若是同一類中的函數有不一樣的簽名,則函數名能夠相同。
class C { public: C( ) { … } // default constructor C( int x ) { … } // convert constructor }; void print( double d ); void print( char * ); int main( ) { C c1,c2(26); print(100.123); print( "100.123" ); }
編譯過程當中綁定函數調用和對應的函數體
46.什麼是多態?
一個組合的希臘詞。含義:一種物質有多種形態。在專業術語中,多態是一種運行時綁定機制(run-time binding) ,經過這種機制,實現將函數名綁定到函數具體實現代碼的目的。
47.什麼是切片(Slicing)?(注意參考講義)
派生類的存儲結構與基類的存儲結構存在着「粘接(splice)」關係:
當子類對象拷貝到父類對象時,父類對象中只存在父類定義的成員,而不會出現任何子類中的成員。
48.使用異常處理機制的好處是什麼?
1.將常規代碼與錯誤處理代碼的分離
2.實如今調用棧中傳播異常
3.實現對不一樣的錯誤類型進行分類
49.簡述C++中的異常處理機制。要捕獲某段代碼中的全部異常,應該如何編寫代碼?
C++ 用try和catch進行異常處理,當try塊出現異常,則catch中匹配相應的異常處理,若catch塊中沒有匹配該異常對象的語句,則轉向外一層的try、catch語句,若一直退回到主函數都沒法處理異常,則由系統調用terminate()函數終止程序。用異常規格(exception specification)列出函數可能會拋出全部異常的類型。
50.分別舉例說明用於算法抽象的函數模板和用於數據抽象的類模板。(google)
函數模板:
template<class T> T add( T a, T b )//函數模板 { return a + b; } add<int> ( 10, 17 );//模板實例 complex c1, c2; add<complex> ( c1, c2 );
函數模板是 對算法類似,但支持的數據類型不一樣的一組操做的提煉,以提升程序的重用性。
函數模板的實例就是一個用於特定類型的普通函數。
一般,編譯器可根據實參類型肯定模板參數;
add ( 10, 17 ); // add<int> (10,17); complex c1, c2; add ( c1, c2 ); // add<complex>(c1,c2);
類模板:
使用類模板使用戶能夠爲類定義一種模式,使得類中的某些數據成員、某些數據成員函數的參數、返回值和局部變量可以取任意類型(包括系統預約義和用戶自定義)
有時候,有兩個或多個類,其功能是相同的,僅僅是數據類型不一樣,可使用模板類。
template<class T>//聲明模板 class Array { T* array; int size; public: Array( int ); T& operator[ ]( int ); }; class charArray { char *array; int size; public: charArray( int ); char& operator[](int); }; class intArray { int *array; int size; public: intArray( int ); int& operator[](int); };
51.dynamic-cast的做用是什麼?試舉例說明。
dynamic_cast < Type-id > ( expression )
該運算符把expression轉換爲type-id類型,而且能夠在運行期間檢測類型轉換是否安全。dynamic_cast要求轉型的目的類型必須是指針或者引用。
將基類指針轉換爲派生類指針,將基類引用轉換爲派生類引用;
轉換是有條件的
若是指針(或引用)所指對象的實際類型與轉換的目的類型兼容,則轉換成功進行;
不然如執行的是指針類型的轉換,則獲得空指針;如執行的是引用類型的轉換,則拋出異常。