C++笔试面试题 从网上整理的,带答案
1.new 、 delete 、 malloc 、 free 關(guān)系
delete 會(huì)調(diào)用對(duì)象的析構(gòu)函數(shù) , 和 new 對(duì)應(yīng) free 只會(huì)釋放內(nèi)存, new 調(diào)用構(gòu)造函數(shù)。 malloc 與 free 是 C++/C 語(yǔ)言的標(biāo)準(zhǔn)庫(kù)函數(shù), new/delete 是 C++ 的運(yùn)算符。它們都可用于申請(qǐng)動(dòng)態(tài)內(nèi)存和釋放內(nèi)存。對(duì)于非內(nèi)部數(shù)據(jù)類型的對(duì)象而言,光用 maloc/free 無法滿足動(dòng)態(tài)對(duì)象的要求。對(duì)象在創(chuàng)建的同時(shí)要自動(dòng)執(zhí)行構(gòu)造函數(shù),對(duì)象在消亡之前要自動(dòng)執(zhí)行析構(gòu)函數(shù)。由于 malloc/free 是庫(kù)函數(shù)而不是運(yùn)算符,不在編譯器控制權(quán)限之內(nèi),不能夠把執(zhí)行構(gòu)造函數(shù)和析構(gòu)函數(shù)的任務(wù)強(qiáng)加于 malloc/free 。因此 C++ 語(yǔ)言需要一個(gè)能完成動(dòng)態(tài)內(nèi)存分配和初始化工作的運(yùn)算符 new ,以及一個(gè)能完成清理與釋放內(nèi)存工作的運(yùn)算符 delete 。注意 new/delete 不是庫(kù)函數(shù)。
總結(jié):new和delete會(huì)自動(dòng)調(diào)用對(duì)象的構(gòu)造與析構(gòu)函數(shù)而malloc與free不會(huì);
new和delete式C++運(yùn)算符,而malloc和free是C/C++標(biāo)準(zhǔn)庫(kù)函數(shù)。
——————————————————————————————–
2.delete 與 delete [] 區(qū)別
delete 只會(huì)調(diào)用一次析構(gòu)函數(shù),而 delete[] 會(huì)調(diào)用每一個(gè)成員的析構(gòu)函數(shù)。在 More Effective C++ 中有更為詳細(xì)的解釋:“當(dāng) delete 操作符用于數(shù)組時(shí),它為每個(gè)數(shù)組元素調(diào)用析構(gòu)函數(shù),然后調(diào)用 operatordelete 來釋放內(nèi)存。” delete 與 New 配套, delete [] 與 new [] 配套
MemTest*mTest1=newMemTest[10];
MemTest*mTest2=newMemTest;
int*pInt1=newint[10];
int*pInt2=newint;
delete[]pInt1; //-1-
delete[]pInt2; //-2-
delete[]mTest1;//-3-
delete[]mTest2;//-4-
在 -4- 處報(bào)錯(cuò)。
這就說明:對(duì)于內(nèi)建簡(jiǎn)單數(shù)據(jù)類型, delete 和 delete[] 功能是相同的。對(duì)于自定義的復(fù)雜數(shù)據(jù)類型, delete 和 delete[] 不能互用。 delete[] 刪除一個(gè)數(shù)組, delete 刪除一個(gè)指針簡(jiǎn)單來說,用 new 分配的內(nèi)存用 delete 刪除用 new[] 分配的內(nèi)存用 delete[] 刪除 delete[] 會(huì)調(diào)用數(shù)組元素的析構(gòu)函數(shù)。內(nèi)部數(shù)據(jù)類型沒有析構(gòu)函數(shù),所以問題不大。如果你在用 delete 時(shí)沒用括號(hào), delete 就會(huì)認(rèn)為指向的是單個(gè)對(duì)象,否則,它就會(huì)認(rèn)為指向的是一個(gè)數(shù)組。
總結(jié): delete 只會(huì)調(diào)用一次析構(gòu)函數(shù),而 delete[] 會(huì)調(diào)用每一個(gè)成員的析構(gòu)函數(shù)。
——————————————————————————————–
3.C C++ JAVA 共同點(diǎn),不同之處?
相同點(diǎn):都是面向?qū)ο蟮恼Z(yǔ)言
不同點(diǎn):c/c++是編譯型語(yǔ)言,還有一些語(yǔ)言完全是解釋型的(如Basie),而java既是編譯型的又是解釋型的語(yǔ)言
c/c++存在指針運(yùn)算,Basie沒有顯示指針,而java有指針,但取消了指針的運(yùn)算
——————————————————————————————–
4. 繼承優(yōu)缺點(diǎn)。
類繼承是在編譯時(shí)刻靜態(tài)定義的,且可直接使用,類繼承可以較方便地改變父類的實(shí)現(xiàn)。但是類繼承也有一些不足之處。首先,因?yàn)槔^承在編譯時(shí)刻就定義了,所以無法在運(yùn)行時(shí)刻改變從父類繼承的實(shí)現(xiàn)。更糟的是,父類通常至少定義了子類的部分行為,父類的任何改變都可能影響子類的行為。如果繼承下來的實(shí)現(xiàn)不適合解決新的問題,則父類必須重寫或被其他更適合的類替換。這種依賴關(guān)系限制了靈活性并最終限制了復(fù)用性。
——————————————————————————————–
5.C++ 有哪些性質(zhì)(面向?qū)ο筇攸c(diǎn))
封裝,繼承和多態(tài)。
在面向?qū)ο蟪绦蛟O(shè)計(jì)語(yǔ)言中,封裝是利用可重用成分構(gòu)造軟件系統(tǒng)的特性,它不僅支持系統(tǒng)的可重用性,而且還有利于提高系統(tǒng)的可擴(kuò)充性;消息傳遞可以實(shí)現(xiàn)發(fā)送一個(gè)通用的消息而調(diào)用不同的方法;封裝是實(shí)現(xiàn)信息隱蔽的一種技術(shù),其目的是使類的定義和實(shí)現(xiàn)分離。
——————————————————————————————–
6. 子類析構(gòu)時(shí)要調(diào)用父類的析構(gòu)函數(shù)嗎?
析構(gòu)函數(shù)調(diào)用的次序是先派生類的析構(gòu)后基類的析構(gòu),也就是說在基類的的析構(gòu)調(diào)用的時(shí)候 , 派生類的信息已經(jīng)全部銷毀了定義一個(gè)對(duì)象時(shí)先調(diào)用基類的構(gòu)造函數(shù)、然后調(diào)用派生類的構(gòu)造函數(shù);析構(gòu)的時(shí)候恰好相反:先調(diào)用派生類的析構(gòu)函數(shù)、然后調(diào)用基類的析構(gòu)函數(shù) JAVA 無析構(gòu)函數(shù)深拷貝和淺拷貝
——————————————————————————————–
7. 多態(tài),虛函數(shù),純虛函數(shù)
這么一大堆名詞,實(shí)際上就圍繞一件事展開,就是多態(tài),其他三個(gè)名詞都是為實(shí)現(xiàn)C++的多態(tài)機(jī)制而提出的一些規(guī)則,下面分兩部分介紹,第一部分介紹【多態(tài)】,第二部分介紹【虛函數(shù),純虛函數(shù),抽象類】
一 【多態(tài)】
多態(tài)的概念 :關(guān)于多態(tài),好幾種說法,好的壞的都有,分別說一下:
1 指同一個(gè)函數(shù)的多種形態(tài)。
個(gè)人認(rèn)為這是一種高手中的高手喜歡的說法,對(duì)于一般開發(fā)人員是一種差的不能再差的概念,簡(jiǎn)直是對(duì)人的誤導(dǎo),然人很容易就靠到函數(shù)重載上了。
以下是個(gè)人認(rèn)為解釋的比較好的兩種說法,意思大體相同:
2多態(tài)是具有表現(xiàn)多種形態(tài)的能力的特征,在OO中是指,語(yǔ)言具有根據(jù)對(duì)象的類型以不同方式處理之,特別是重載方法和繼承類這種形式的能力。
這種說法有點(diǎn)繞,仔細(xì)想想,這才是C++要告訴我們的。
3多態(tài)性是允許你將父對(duì)象設(shè)置成為和一個(gè)或更多的他的子對(duì)象相等的技術(shù),賦值之后,父對(duì)象就可以根據(jù)當(dāng)前賦值給它的子對(duì)象的特性以不同的方式運(yùn)作。簡(jiǎn)單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。多態(tài)性在Object Pascal和C++中都是通過虛函數(shù)(Virtual Function) 實(shí)現(xiàn)的。
這種說法看來是又易懂,又全面的一種,尤其是最后一句,直接點(diǎn)出了虛函數(shù)與多態(tài)性的關(guān)系,如果你還是不太懂,沒關(guān)系,再把3讀兩遍,有個(gè)印象,往后看吧。
- – - – - – - – - – - – - – - – - – - – - – - – - – - – - – -
二 【虛函數(shù),純虛函數(shù),抽象類】
多態(tài)才說了個(gè)概念,有什么用還沒說就進(jìn)入第二部分了?看看概念3的最后一句,虛函數(shù)就是為多態(tài)而生的,多態(tài)的作用的介紹和虛函數(shù)簡(jiǎn)直關(guān)系太大了,就放一起說吧。
多態(tài)的作用:繼承是子類使用父類的方法,而多態(tài)則是父類使用子類的方法。這是一句大白話,多態(tài)從用法上就是要用父類(確切的說是父類的對(duì)象名)去調(diào)用子類的方法,例如:
【例一】
class A {
public:
A() {}
(virtual) void print() {
cout << “This is A.” << endl;
}
};
class B : public A {
public:
B() {}
void print() {
cout << “This is B.” << endl; } }; int main(int argc, char* argv[]) { B b; A a; a = b;a.print;—————————————- make1 // A &a = b; a->print();———————————-make2
//A *a = new B();a->print();——————————–make3
return 0;
}
這將顯示:
This is B.
如果把virtual去掉,將顯示:
This is A.
(make1,2,3分別是對(duì)應(yīng)兼容規(guī)則(后面介紹)的三種方式,調(diào)用結(jié)果是一樣的)
加上virtual ,多態(tài)了,B中的print被調(diào)用了,也就是可以實(shí)現(xiàn)父類使用子類的方法。
對(duì)多態(tài)的作用有一個(gè)初步的認(rèn)識(shí)了之后,再提出更官方,也是更準(zhǔn)確的對(duì)多態(tài)作用的描述:
多態(tài)性使得能夠利用同一類(基類)類型的指針來引用不同類的對(duì)象,以及根據(jù)所引用對(duì)象的不同,以不同的方式執(zhí)行相同的操作。把不同的子類對(duì)象都當(dāng)作父類來看,可以屏蔽不同子類對(duì)象之間的差異,寫出通用的代碼,做出通用的編程,以適應(yīng)需求的不斷變化。賦值之后,父對(duì)象就可以根據(jù)當(dāng)前賦值給它的子對(duì)象的特性以不同的方式運(yùn)作(也就是可以調(diào)用子對(duì)象中對(duì)父對(duì)象的相關(guān)函數(shù)的改進(jìn)方法)。
那么上面例子中為什么去掉virtual就調(diào)用的不是B中的方法了呢,明明把B的對(duì)象賦給指針a了啊,是因?yàn)镃++定義了一組對(duì)象賦值的兼容規(guī)則,就是指在公有派生的情況下,對(duì)于某些場(chǎng)合,一個(gè)派生類的對(duì)象可以作為基類對(duì)象來使用,具體來說,就是下面三種情形:
Class A ;
class B:public A
1. 派生的對(duì)象可以賦給基類的對(duì)象
A a;
B b;
a = b;
2. 派生的對(duì)象可以初始化基類的引用
B b;
A &a = b;
3. 派生的對(duì)象的地址可以賦給指向基類的指針
B b;
A *a = &b;
或
A *a = new B();
由上述對(duì)象賦值兼容規(guī)則可知,一個(gè)基類的對(duì)象可兼容派生類的對(duì)象,一個(gè)基類的指針可指向派生類的對(duì)象,一個(gè)基類的引用可引用派生類的對(duì)象,于是對(duì)于通過基類的對(duì)象指針(或引用)對(duì)成員函數(shù)的調(diào)用,編譯時(shí)無法確定對(duì)象的類,而只是在運(yùn)行時(shí)才能確定并由此確定調(diào)用哪個(gè)類中的成員函數(shù)。
看看剛才的例子,根據(jù)兼容規(guī)則,B的對(duì)象根本就被當(dāng)成了A的對(duì)象來使用,難怪B的方法不能被調(diào)用。
【例二】
#include
using namespace std;
class A
{
public:
void (virtual) print(){cout << “A print”<
private:
};
class B : public A
{
public:
void print(){cout << “B print”< private:
};
void test(A &tmpClass)
{
tmpClass.print();
}
int main(void)
{
B b;
test(b);
getchar();
return 0;
}
這將顯示:
B print
如果把virtual去掉,將顯示:
A print
那么,為什么加了一個(gè)virtual以后就達(dá)到調(diào)用的目的了呢,多態(tài)了嘛~那么為什么加上virtual就多態(tài)了呢,我們還要介紹一個(gè)概念:聯(lián)編
函數(shù)的聯(lián)編:在編譯或運(yùn)行將函數(shù)調(diào)用與相應(yīng)的函數(shù)體連接在一起的過程。
1 先期聯(lián)編或靜態(tài)聯(lián)編:在編譯時(shí)就能進(jìn)行函數(shù)聯(lián)編稱為先期聯(lián)編或靜態(tài)聯(lián)編。
2 遲后聯(lián)編或動(dòng)態(tài)聯(lián)編:在運(yùn)行時(shí)才能進(jìn)行的聯(lián)編稱為遲后聯(lián)編或動(dòng)態(tài)聯(lián)編。
那么聯(lián)編與虛函數(shù)有什么關(guān)系呢,當(dāng)然,造成上面例子中的矛盾的原因就是代碼的聯(lián)編過程采用了先期聯(lián)編,使得編譯時(shí)系統(tǒng)無法確定究竟應(yīng)該調(diào)用基類中的函數(shù)還是應(yīng)該調(diào)用派生類中的函數(shù),要是能夠采用上面說的遲后聯(lián)編就好了,可以在運(yùn)行時(shí)再判斷到底是哪個(gè)對(duì)象,所以,virtual關(guān)鍵字的作用就是提示編譯器進(jìn)行遲后聯(lián)編,告訴連接過程:“我是個(gè)虛的,先不要連接我,等運(yùn)行時(shí)再說吧”。
那么為什么連接的時(shí)候就知道到底是哪個(gè)對(duì)象了呢,這就引出虛函數(shù)的原理了:當(dāng)編譯器遇到virtual后,會(huì)為所在的類構(gòu)造一個(gè)表和一個(gè)指針,那個(gè)表叫做vtbl,每個(gè)類都有自己的vtbl,vtbl的作用就是保存自己類中虛函數(shù)的地址,我們可以把vtbl形象地看成一個(gè)數(shù)組,這個(gè)數(shù)組的每個(gè)元素存放的就是虛函數(shù)的地址.指針叫做vptr,指向那個(gè)表。而這個(gè)指針保存在相應(yīng)的對(duì)象當(dāng)中,也就是說只有創(chuàng)建了對(duì)象以后才能找到相應(yīng)虛函數(shù)的地址。
【注意】
1為確保運(yùn)行時(shí)的多態(tài)定義的基類與派生類的虛函數(shù)不僅函數(shù)名要相同,其返回值及參數(shù)都必須相同,否則即使加上了virtual,系統(tǒng)也不進(jìn)行遲后聯(lián)編。
2 虛函數(shù)關(guān)系通過繼承關(guān)系自動(dòng)傳遞給基類中同名的函數(shù),也就是上例中如果A中print有virtual,那么 B中的print即使不加virtual,也被自動(dòng)認(rèn)為是虛函數(shù)。
*3 沒有繼承關(guān)系,多態(tài)機(jī)制沒有意義,繼承必須是公有繼承。
*4現(xiàn)實(shí)中,遠(yuǎn)不只我舉的這兩個(gè)例子,但是大的原則都是我前面說到的“如果發(fā)現(xiàn)一個(gè)函數(shù)需要在派生類里有不同的表現(xiàn),那么它就應(yīng)該是虛的”。這句話也可以反過來說:“如果你發(fā)現(xiàn)基類提供了虛函數(shù),那么你最好override它”。
純虛函數(shù):
虛函數(shù)的作用是為了實(shí)現(xiàn)對(duì)基類與派生類中的虛函數(shù)成員的遲后聯(lián)編,而純虛函數(shù)是表明不具體實(shí)現(xiàn)的虛函數(shù)成員,即純虛函數(shù)無實(shí)現(xiàn)代碼。其作用僅僅是為其派生類提過一個(gè)統(tǒng)一的構(gòu)架,具體實(shí)現(xiàn)在派生類中給出。
一個(gè)函數(shù)聲明為純虛后,純虛函數(shù)的意思是:我是一個(gè)抽象類!不要把我實(shí)例化!純虛函數(shù)用來規(guī)范派生類的行為,實(shí)際上就是所謂的“接口”。它告訴使用者,我的派生類都會(huì)有這個(gè)函數(shù)。
抽象類:
含有一個(gè)或多個(gè)純虛函數(shù)的類稱為抽象類。
【例三】
#include
using namespace std;
class A
{
public:
virtual float print() = 0;
protected:
float h,w;
private:
};
class B : public A
{
public:
B(float h0,float w0){h = h0;w = w0;}
float print(){return h*w;}
private:
};
class C : public A
{
public:
C(float h0,float w0){h = h0;w = w0;}
float print(){return h*w/2;}
private:
};
int main(void)
{
A *a1,*a2;
B b(1,2);
C c(1,2);
a1 = &b;
a2 = &c;
cout << a1->print()<print()<print;a2->print就不能用了),給多態(tài)性造成不便,這里要強(qiáng)調(diào)的是,我們是希望用基類的指針調(diào)用派生類的方法,希望用到多態(tài)機(jī)制,如果讀者并不想用基類指針,認(rèn)為用b,c指針直接調(diào)用更好,那純虛函數(shù)就沒有意義了,多態(tài)也就沒有意義了,了解一下多態(tài)的好處,再?zèng)Q定是否用純虛函數(shù)吧。
【注意】
1 抽象類并不能直接定義對(duì)象,只可以如上例那樣聲明指針,用來指向基類派生的子類的對(duì)象,上例中的A *a1,*a2;該為 A a1,a2;是錯(cuò)誤的。
2 從一個(gè)抽象類派生的類必須提供純虛函數(shù)的代碼實(shí)現(xiàn)或依舊指明其為派生類,否則是錯(cuò)誤的。
3 當(dāng)一個(gè)類打算被用作其它類的基類時(shí),它的析構(gòu)函數(shù)必須是虛的。
【例三】
class A
{
public:
A() { ptra_ = new char[10];}
~A() { delete[] ptra_;} // 非虛析構(gòu)函數(shù)
private:
char * ptra_;
};
class B: public A
{
public:
B() { ptrb_ = new char[20];}
~B() { delete[] ptrb_;}
private:
char * ptrb_;
};
void foo()
{
A * a = new B;
delete a;
}
在這個(gè)例子中,程序也許不會(huì)象你想象的那樣運(yùn)行,在執(zhí)行delete a的時(shí)候,實(shí)際上只有A::~A()被調(diào)用了,而B類的析構(gòu)函數(shù)并沒有被調(diào)用!這是否有點(diǎn)兒可怕? 如果將上面A::~A()改為virtual,就可以保證B::~B()也在delete a的時(shí)候被調(diào)用了。因此基類的析構(gòu)函數(shù)都必須是virtual的。純虛的析構(gòu)函數(shù)并沒有什么作用,是虛的就夠了。通常只有在希望將一個(gè)類變成抽象類(不能實(shí)例化的類),而這個(gè)類又沒有合適的函數(shù)可以被純虛化的時(shí)候,可以使用純虛的析構(gòu)函數(shù)來達(dá)到目的。
最后通過一個(gè)例子說明一下抽象類,純虛函數(shù)以及多態(tài)的妙用吧:
我們希望通過一個(gè)方法得到不同圖形面積的和的方式:
#include
using namespace std;
class A //定義一個(gè)抽象類,用來求圖形面積
{
public:
virtual float area() = 0;//定義一個(gè)計(jì)算面積的純虛函數(shù),圖形沒確定,當(dāng)
//不能確定具體實(shí)現(xiàn)
protected:
float h,w; //這里假設(shè)所有圖形的面積都可以用h和w兩個(gè)元素計(jì)算得出
//就假設(shè)為高和長(zhǎng)吧
private:
};
class B : public A //定義一個(gè)求長(zhǎng)方形面積的類
{
public:
B(float h0,float w0){h = h0;w = w0;}
float area (){return h*w;}//基類純虛函數(shù)的具體實(shí)現(xiàn)
private:
};
class C : public A //定義一個(gè)求三角形面積的類
{
public:
C(float h0,float w0){h = h0;w = w0;}
float area (){return h*w/2;}//基類純虛函數(shù)的具體實(shí)現(xiàn)
private:
};
float getTotal(A *s[],int n)//通過一個(gè)數(shù)組傳遞所有的圖形對(duì)象
//多態(tài)的好處出來了吧,不是多態(tài),不能用基類A調(diào)用
//參數(shù)類型怎么寫,要是有100個(gè)不同的圖形,怎么傳遞
{
float sum = 0;
for(int i = 0;i < n; i++) sum = sum + s[i]->area();
return sum;
}
int main(void)
{
float totalArea;
A *a[2];
a[0] = new B(1,2); //一個(gè)長(zhǎng)方形對(duì)象
a[1] = new C(1,2);//一個(gè)三角形對(duì)象
totalArea = getTotal(a , 2);//求出兩個(gè)對(duì)象的面積和
getchar();
return 0;
}
——————————————————————————————–
8. 求下面函數(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ù)。
——————————————————————————————–
9. 什么是 “ 引用 ” ?申明和使用 “ 引用 ” 要注意哪些問題?
答:引用就是某個(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ù)類型,因此引用本身不占存儲(chǔ)單元,系統(tǒng)也不給引用分配存儲(chǔ)單元。不能建立數(shù)組的引用。
——————————————————————————————–
10. 將 “ 引用 ” 作為函數(shù)參數(shù)有哪些特點(diǎn)?
( 1 )傳遞引用給函數(shù)與傳遞指針的效果是一樣的。這時(shí),被調(diào)函數(shù)的形參就成為原來主調(diào)函數(shù)中的實(shí)參變量或?qū)ο蟮囊粋€(gè)別名來使用,所以在被調(diào)函數(shù)中對(duì)形參變量的操作就是對(duì)其相應(yīng)的目標(biāo)對(duì)象(在主調(diào)函數(shù)中)的操作。
( 2 )使用引用傳遞函數(shù)的參數(shù),在內(nè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í)參。而引用更容易使用,更清晰。
——————————————————————————————–
11. 在什么時(shí)候需要使用 “ 常引用 ” ?
如果既要利用引用提高程序的效率,又要保護(hù)傳遞給函數(shù)的數(shù)據(jù)不在函數(shù)中被改變,就應(yīng)使用常引用。常引用聲明方式: const 類型標(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 類型的。因此上面的表達(dá)式就是試圖將一個(gè) const 類型的對(duì)象轉(zhuǎn)換為非 const 類型,這是非法的。引用型參數(shù)應(yīng)該在能被定義為 const 的情況下,盡量定義為 const 。
——————————————————————————————–
12. 將 “ 引用 ” 作為函數(shù)返回值類型的格式、好處和需要遵守的規(guī)則 ?
格式:類型標(biāo)識(shí)符 & 函數(shù)名(形參列表及類型說明) { // 函數(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++[1] 的 Item 31 。主要原因是局部變量會(huì)在函數(shù)返回后被銷毀,因此被返回的引用就成為了 ” 無所指 ” 的引用,程序會(huì)進(jìn)入未知狀態(tài)。
( 2 )不能返回函數(shù)內(nèi)部 new 分配的內(nèi)存的引用。這條可以參照 Effective C++[1] 的 Item 31 。雖然不存在局部變量的被動(dòng)銷毀問題,可對(duì)于這種情況(返回函數(shù)內(nèi)部 new 分配內(nèi)存的引用),又面臨其它尷尬局面。例如,被函數(shù)返回的引用只是作為一個(gè)臨時(shí)變量出現(xiàn),而沒有被賦予一個(gè)實(shí)際的變量,那么這個(gè)引用所指向的空間(由 new 分配)就無法釋放,造成 memory leak 。
( 3 )可以返回類成員的引用,但最好是 const 。這條原則可以參照 Effective C++[1] 的 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è)操作符的流引用。可選的其它方案包括:返回一個(gè)流對(duì)象和返回一個(gè)流對(duì)象指針。但是對(duì)于返回一個(gè)流對(duì)象,程序必須重新(拷貝)構(gòu)造一個(gè)新的流對(duì)象,也就是說,連續(xù)的兩個(gè) << 操作符實(shí)際上是針對(duì)不同對(duì)象的!這無法讓人接受。對(duì)于返回一個(gè)流指針則不能連續(xù)使用 << 操作符。因此,返回一個(gè)流對(duì)象引用是惟一選擇。這個(gè)唯一選擇很關(guān)鍵,它說明了引用的重要性以及無可替代性,也許這就是 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< cout<=0 && n else { cout< 6″) : puts(” return 0;
}
——————————————————————————————–
114. 交換兩個(gè)數(shù)的宏定義
交換兩個(gè)參數(shù)值的 宏定義 為: . #define SWAP (a,b) (a)=(a)+(b);(b)=(a)-(b);(a)=(a)-(b);
——————————————————————————————–
115.Itearator 各指針的區(qū)別
游標(biāo)和指針
我說過游標(biāo)是指針,但不僅僅是指針。游標(biāo)和指針很像,功能很像指針,但是實(shí)際上,游標(biāo)是通過重載一元的 ”*” 和 ”->” 來從容器中間接地返回一個(gè)值。將這些值存儲(chǔ)在容器中并不是一個(gè)好主意,因?yàn)槊慨?dāng)一個(gè)新值添加到容器中或者有一個(gè)值從容器中刪除,這些值就會(huì)失效。在某種程度上,游標(biāo)可以看作是句柄( handle )。通常情況下游標(biāo)( iterator )的類型可以有所變化,這樣容器也會(huì)有幾種不同方式的轉(zhuǎn)變:
iterator—— 對(duì)于除了 vector 以外的其他任何容器,你可以通過這種游標(biāo)在一次操作中在容器中朝向前的方向走一步。這意味著對(duì)于這種游標(biāo)你只能使用 “++” 操作符。而不能使用 “–” 或 “+=” 操作符。而對(duì)于 vector 這一種容器,你可以使用 “+=” 、 “—” 、 “++” 、 “-=” 中的任何一種操作符和 “” 、 “>=” 、 “==” 、 “!=” 等比較運(yùn)算符。
——————————————————————————————–
116. C++ 中的 class 和 struct 的區(qū)別
從語(yǔ)法上,在 C++ 中(只討論 C++ 中)。 class 和 struct 做類型定義時(shí)只有兩點(diǎn)區(qū)別:
(一)默認(rèn)繼承權(quán)限。如果不明確指定,來自 class 的繼承按照 private 繼承處理,來自 struct 的繼承按照 public 繼承處理;
(二)成員的默認(rèn)訪問權(quán)限。 class 的成員默認(rèn)是 private 權(quán)限, struct 默認(rèn)是 public 權(quán)限。
除了這兩點(diǎn), class 和 struct 基本就是一個(gè)東西。語(yǔ)法上沒有任何其它區(qū)別。
不能因?yàn)閷W(xué)過 C 就總覺得連 C++ 中 struct 和 class 都區(qū)別很大,下面列舉的說明可能比較無聊,因?yàn)?struct 和 class 本來就是基本一樣的東西,無需多說。但這些說明可能有助于澄清一些常見的關(guān)于 struct 和 class 的錯(cuò)誤認(rèn)識(shí):
( 1 )都可以有成員函數(shù);包括各類構(gòu)造函數(shù),析構(gòu)函數(shù),重載的運(yùn)算符,友元類,友元結(jié)構(gòu),友元函數(shù),虛函數(shù),純虛函數(shù),靜態(tài)函數(shù);
( 2 )都可以有一大堆 public/private/protected 修飾符在里邊;
( 3 )雖然這種風(fēng)格不再被提倡,但語(yǔ)法上二者都可以使用大括號(hào)的方式初始化:
A a = {1, 2, 3}; 不管 A 是個(gè) struct 還是個(gè) class ,前提是這個(gè)類 / 結(jié)構(gòu)足夠簡(jiǎn)單,比如所有的成員都是 public 的,所有的成員都是簡(jiǎn)單類型,沒有顯式聲明的構(gòu)造函數(shù)。
( 4 )都可以進(jìn)行復(fù)雜的繼承甚至多重繼承,一個(gè) struct 可以繼承自一個(gè) class ,反之亦可;一個(gè) struct 可以同時(shí)繼承 5 個(gè) class 和 5 個(gè) struct ,雖然這樣做不太好。
( 5 )如果說 class 的設(shè)計(jì)需要注意 OO 的原則和風(fēng)格,那么沒任何理由說設(shè)計(jì) struct 就不需要注意。
( 6 )再次說明,以上所有說法都是指在 C++ 語(yǔ)言中,至于在 C 里的情況, C 里是根本沒有 “class” ,而 C 的 struct 從根本上也只是個(gè)包裝數(shù)據(jù)的語(yǔ)法機(jī)制。
—————————————————————
最后,作為語(yǔ)言的兩個(gè)關(guān)鍵字,除去定義類型時(shí)有上述區(qū)別之外,另外還有一點(diǎn)點(diǎn): “class” 這個(gè)關(guān)鍵字還用于定義模板參數(shù),就像 “typename” 。但關(guān)鍵字 “struct” 不用于定義模板參數(shù)。
在模版中,類型參數(shù)前面可以使用class或typename,如果使用struct,則含義不同,struct后面跟的是“non-type template parameter”,而class或typename后面跟的是類型參數(shù)。
template
void f(X x)
{
}
//出錯(cuò)信息:d:codecpptestcpptestcpptest.cpp(33) : error C2065: ‘X’ : undeclared identifier
關(guān)于使用大括號(hào)初始化
class 和 struct 如果定義了構(gòu)造函數(shù)的話,都不能用大括號(hào)進(jìn)行初始化
如果沒有定義構(gòu)造函數(shù), struct 可以用大括號(hào)初始化。
如果沒有定義構(gòu)造函數(shù),且所有成員變量全是 public 的話,可以用大括號(hào)初始化。
關(guān)于默認(rèn)訪問權(quán)限
class 中默認(rèn)的成員訪問權(quán)限是 private 的,而 struct 中則是 public 的。
關(guān)于繼承方式
class 繼承默認(rèn)是 private 繼承,而 struct 繼承默認(rèn)是 public 繼承。
關(guān)于模版
在模版中,類型參數(shù)前面可以使用 class 或 typename ,如果使用 struct ,則含義不同, struct 后面跟的是 “non-type template parameter” ,而 class 或 typename 后面跟的是類型參數(shù)。
class 中有個(gè)默認(rèn)的 this 指針, struct 沒有
不同點(diǎn):構(gòu)造函數(shù),析構(gòu)函數(shù) this 指針
——————————————————————————————–
117. 有關(guān)重載函數(shù)
返回值類型不同構(gòu)不成重載
參數(shù)參數(shù)順序不同能構(gòu)成重載
c++ 函數(shù)同名不同返回值不算重載!函數(shù)重載是忽略返回值類型的。
成員函數(shù)被重載的特征有:
1) 相同的范圍(在同一個(gè)類中);
2) 函數(shù)名字相同;
3) 參數(shù)不同;
4) virtual 關(guān)鍵字可有可無。
5) 成員函數(shù)中 有無 const ( 函數(shù)后面 ) 也可判斷是否重載
——————————————————————————————–
118. 數(shù)據(jù)庫(kù)與 T-SQL 語(yǔ)言
關(guān)系數(shù)據(jù)庫(kù)是表的集合,它是由一個(gè)或多個(gè)關(guān)系模式定義。 SQL 語(yǔ)言中的數(shù)據(jù)定義功能包括對(duì)數(shù)據(jù)庫(kù)、基本表、視圖、索引的定義。
——————————————————————————————–
119. 關(guān)系模型的基本概念
關(guān)系數(shù)據(jù)庫(kù)以關(guān)系模型為基礎(chǔ),它有以下三部分組成:
● 數(shù)據(jù)結(jié)構(gòu) —— 模型所操作的對(duì)象、類型的集合
● 完整性規(guī)則 —— 保證數(shù)據(jù)有效、正確的約束條件
● 數(shù)據(jù)操作 —— 對(duì)模型對(duì)象所允許執(zhí)行的操作方式
關(guān)系( Relation )是一個(gè)由行和列組成的二維表格,表中的每一行是一條記錄( Record ),每一列是記錄的一個(gè)字段( Field )。表中的每一條記錄必須是互斥的,字段的值必須具有原子性。
——————————————————————————————–
120.SQL 語(yǔ)言概述
SQL (結(jié)構(gòu)化查詢語(yǔ)言)是關(guān)系數(shù)據(jù)庫(kù)語(yǔ)言的一種國(guó)際標(biāo)準(zhǔn),它是一種非過程化的語(yǔ)言。通過編寫 SQL ,我們可以實(shí)現(xiàn)對(duì)關(guān)系數(shù)據(jù)庫(kù)的全部操作。
● 數(shù)據(jù)定義語(yǔ)言( DDL ) —— 建立和管理數(shù)據(jù)庫(kù)對(duì)象
● 數(shù)據(jù)操縱語(yǔ)言( DML ) —— 用來查詢與更新數(shù)據(jù)
● 數(shù)據(jù)控制語(yǔ)言( DCL ) —— 控制數(shù)據(jù)的安全性
起來是一個(gè)很簡(jiǎn)單的問題,每一個(gè)使用過 RDBMS 的人都會(huì)有一個(gè)概念。
事務(wù)處理系統(tǒng)的典型特點(diǎn)是具備 ACID 特征。 ACID 指的是 Atomic (原子的)、 Consistent (一致的)、 Isolated (隔離的)以及 Durable (持續(xù)的),它們代表著事務(wù)處理應(yīng)該具備的四個(gè)特征:
原子性:組成事務(wù)處理的語(yǔ)句形成了一個(gè)邏輯單元,不能只執(zhí)行其中的一部分
一致性:在事務(wù)處理執(zhí)行之前和之后,數(shù)據(jù)是一致的。
隔離性:一個(gè)事務(wù)處理對(duì)另一個(gè)事務(wù)處理沒有影響。
持續(xù)性:當(dāng)事務(wù)處理成功執(zhí)行到結(jié)束的時(shí)候,其效果在數(shù)據(jù)庫(kù)中被永久紀(jì)錄下來。
——————————————————————————————–
121.C 語(yǔ)言中結(jié)構(gòu)化程序設(shè)計(jì)的三種基本控制結(jié)構(gòu)
順序結(jié)構(gòu)
選擇結(jié)構(gòu)
循環(huán)結(jié)構(gòu)
——————————————————————————————–
122.CVS 是什么
cvs ( Concurrent Version System ) 是一個(gè)版本控制系統(tǒng)。使用它,可以記錄下你的源文件的歷史。
例如,修改軟件時(shí)可能會(huì)不知不覺混進(jìn)一些 bug ,而且可能過了很久你才會(huì)察覺到它們的存在。有了 cvs ,你可以很容易地恢復(fù)舊版本,并從中看出到底是哪個(gè)修改導(dǎo)致了這個(gè) bug 。有時(shí)這是很有用的。
CVS 服務(wù)器端對(duì)每個(gè)文件維護(hù)著一個(gè)修訂號(hào) , 每次對(duì)文件的更新,都會(huì)使得文件的修訂號(hào)加 1 。在客戶端中也對(duì)每個(gè)文件維護(hù)著一個(gè)修訂號(hào) ,CVS 通過這兩個(gè)修訂號(hào)的關(guān)系,來進(jìn)行 Update,Commit 和發(fā)現(xiàn)沖突等操作操作
——————————————————————————————–
123. 三種基本的數(shù)據(jù)模型
按照數(shù)據(jù)結(jié)構(gòu)類型的不同,將數(shù)據(jù)模型劃分為層次模型、網(wǎng)狀模型和關(guān)系模型。
——————————————————————————————–
總結(jié)
以上是生活随笔為你收集整理的C++笔试面试题 从网上整理的,带答案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dcs常用的冗余方式_DCS冗余技术简介
- 下一篇: 笔试3