'linux C/C++服務器後臺開發面試題總結(編程語言篇)'

"
"
linux C/C++服務器後臺開發面試題總結(編程語言篇)

整篇分為四部分,篇幅較長,建議先收藏。

一、編程語言

1.根據熟悉的語言,談談兩種語言的區別?

主要淺談下C/C++和PHP語言的區別:

1)PHP弱類型語言,一種腳本語言,對數據的類型不要求過多,較多的應用於Web應用開發,現在好多互聯網開發公司的主流web後臺開發語言,主要框架為mvc模型,如smarty,yaf,升級的PHP7速度較快,對服務器的壓力要小很多,在新浪微博已經有應用,對比很明顯。

2)C/C++開發語言,C語言更偏向硬件底層開發,C++語言是目前為止我認為語法內容最多的一種語言。C/C++在執行速度上要快很多,畢竟其他類型的語言大都是C開發的,更多應用於網絡編程和嵌入式編程。

2.volatile是幹啥用的,(必須將cpu的寄存器緩存機制回答的很透徹),使用實例有哪些?(重點)

1)訪問寄存器比訪問內存單元要快,編譯器會優化減少內存的讀取,可能會讀髒數據。聲明變量為volatile,編譯器不再對訪問該變量的代碼優化,仍然從內存讀取,使訪問穩定。

總結:volatile關鍵詞影響編譯器編譯的結果,用volatile聲明的變量表示該變量隨時可能發生變化,與該變量有關的運算,不再編譯優化,以免出錯。

2)使用實例如下(區分C程序員和嵌入式系統程序員的最基本的問題。):

並行設備的硬件寄存器(如:狀態寄存器)

一箇中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)

多線程應用中被幾個任務共享的變量

3)一個參數既可以是const還可以是volatile嗎?解釋為什麼。

可以。一個例子是隻讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。

4)一個指針可以是volatile 嗎?解釋為什麼。

可以。儘管這並不很常見。一個例子當中斷服務子程序修該一個指向一個buffer的指針時。

下面的函數有什麼錯誤:

int square(volatile int *ptr) {
return *ptr * *ptr;
}

下面是答案:

這段代碼有點變態。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由於*ptr指向一個volatile型參數,編譯器將產生類似下面的代碼:

int square(volatile int *ptr){
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}

由於*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:

long square(volatile int *ptr){
int a;
a = *ptr;
return a * a;
}

3.static const等等的用法,(能說出越多越好)(重點)

² 首先說說const的用法(絕對不能說是常數)

1)在定義的時候必須進行初始化

2)指針可以是const 指針,也可以是指向const對象的指針

3)定義為const的形參,即在函數內部是不能被修改的

4)類的成員函數可以被聲明為常成員函數,不能修改類的成員變量

5)類的成員函數可以返回的是常對象,即被const聲明的對象

6)類的成員變量是常成員變量不能在聲明時初始化,必須在構造函數的列表裡進行初始化

(注:千萬不要說const是個常數,會被認為是外行人的!!!!哪怕說個只讀也行)

下面的聲明都是什麼意思?

const int a; a是一個常整型數

int const a; a是一個常整型數

const int *a; a是一個指向常整型數的指針,整型數是不可修改的,但指針可以

int * const a; a為指向整型數的常指針,指針指向的整型數可以修改,但指針是不可修改的

int const * a const; a是一個指向常整型數的常指針,指針指向的整型數是不可修改的,同時指針也是不可修改的

通過給優化器一些附加的信息,使用關鍵字const也許能產生更緊湊的代碼。合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數,防止其被無意的代碼修改。簡而言之,這樣可以減少bug的出現。

Const如何做到只讀?

這些在編譯期間完成,對於內置類型,如int, 編譯器可能使用常數直接替換掉對此變量的引用。而對於結構體不一定。

c/c++linux後臺開發技術學習視頻,後臺私信【視頻】獲取

"
linux C/C++服務器後臺開發面試題總結(編程語言篇)

整篇分為四部分,篇幅較長,建議先收藏。

一、編程語言

1.根據熟悉的語言,談談兩種語言的區別?

主要淺談下C/C++和PHP語言的區別:

1)PHP弱類型語言,一種腳本語言,對數據的類型不要求過多,較多的應用於Web應用開發,現在好多互聯網開發公司的主流web後臺開發語言,主要框架為mvc模型,如smarty,yaf,升級的PHP7速度較快,對服務器的壓力要小很多,在新浪微博已經有應用,對比很明顯。

2)C/C++開發語言,C語言更偏向硬件底層開發,C++語言是目前為止我認為語法內容最多的一種語言。C/C++在執行速度上要快很多,畢竟其他類型的語言大都是C開發的,更多應用於網絡編程和嵌入式編程。

2.volatile是幹啥用的,(必須將cpu的寄存器緩存機制回答的很透徹),使用實例有哪些?(重點)

1)訪問寄存器比訪問內存單元要快,編譯器會優化減少內存的讀取,可能會讀髒數據。聲明變量為volatile,編譯器不再對訪問該變量的代碼優化,仍然從內存讀取,使訪問穩定。

總結:volatile關鍵詞影響編譯器編譯的結果,用volatile聲明的變量表示該變量隨時可能發生變化,與該變量有關的運算,不再編譯優化,以免出錯。

2)使用實例如下(區分C程序員和嵌入式系統程序員的最基本的問題。):

並行設備的硬件寄存器(如:狀態寄存器)

一箇中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)

多線程應用中被幾個任務共享的變量

3)一個參數既可以是const還可以是volatile嗎?解釋為什麼。

可以。一個例子是隻讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。

4)一個指針可以是volatile 嗎?解釋為什麼。

可以。儘管這並不很常見。一個例子當中斷服務子程序修該一個指向一個buffer的指針時。

下面的函數有什麼錯誤:

int square(volatile int *ptr) {
return *ptr * *ptr;
}

下面是答案:

這段代碼有點變態。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由於*ptr指向一個volatile型參數,編譯器將產生類似下面的代碼:

int square(volatile int *ptr){
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}

由於*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:

long square(volatile int *ptr){
int a;
a = *ptr;
return a * a;
}

3.static const等等的用法,(能說出越多越好)(重點)

² 首先說說const的用法(絕對不能說是常數)

1)在定義的時候必須進行初始化

2)指針可以是const 指針,也可以是指向const對象的指針

3)定義為const的形參,即在函數內部是不能被修改的

4)類的成員函數可以被聲明為常成員函數,不能修改類的成員變量

5)類的成員函數可以返回的是常對象,即被const聲明的對象

6)類的成員變量是常成員變量不能在聲明時初始化,必須在構造函數的列表裡進行初始化

(注:千萬不要說const是個常數,會被認為是外行人的!!!!哪怕說個只讀也行)

下面的聲明都是什麼意思?

const int a; a是一個常整型數

int const a; a是一個常整型數

const int *a; a是一個指向常整型數的指針,整型數是不可修改的,但指針可以

int * const a; a為指向整型數的常指針,指針指向的整型數可以修改,但指針是不可修改的

int const * a const; a是一個指向常整型數的常指針,指針指向的整型數是不可修改的,同時指針也是不可修改的

通過給優化器一些附加的信息,使用關鍵字const也許能產生更緊湊的代碼。合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數,防止其被無意的代碼修改。簡而言之,這樣可以減少bug的出現。

Const如何做到只讀?

這些在編譯期間完成,對於內置類型,如int, 編譯器可能使用常數直接替換掉對此變量的引用。而對於結構體不一定。

c/c++linux後臺開發技術學習視頻,後臺私信【視頻】獲取

linux C/C++服務器後臺開發面試題總結(編程語言篇)

"
linux C/C++服務器後臺開發面試題總結(編程語言篇)

整篇分為四部分,篇幅較長,建議先收藏。

一、編程語言

1.根據熟悉的語言,談談兩種語言的區別?

主要淺談下C/C++和PHP語言的區別:

1)PHP弱類型語言,一種腳本語言,對數據的類型不要求過多,較多的應用於Web應用開發,現在好多互聯網開發公司的主流web後臺開發語言,主要框架為mvc模型,如smarty,yaf,升級的PHP7速度較快,對服務器的壓力要小很多,在新浪微博已經有應用,對比很明顯。

2)C/C++開發語言,C語言更偏向硬件底層開發,C++語言是目前為止我認為語法內容最多的一種語言。C/C++在執行速度上要快很多,畢竟其他類型的語言大都是C開發的,更多應用於網絡編程和嵌入式編程。

2.volatile是幹啥用的,(必須將cpu的寄存器緩存機制回答的很透徹),使用實例有哪些?(重點)

1)訪問寄存器比訪問內存單元要快,編譯器會優化減少內存的讀取,可能會讀髒數據。聲明變量為volatile,編譯器不再對訪問該變量的代碼優化,仍然從內存讀取,使訪問穩定。

總結:volatile關鍵詞影響編譯器編譯的結果,用volatile聲明的變量表示該變量隨時可能發生變化,與該變量有關的運算,不再編譯優化,以免出錯。

2)使用實例如下(區分C程序員和嵌入式系統程序員的最基本的問題。):

並行設備的硬件寄存器(如:狀態寄存器)

一箇中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)

多線程應用中被幾個任務共享的變量

3)一個參數既可以是const還可以是volatile嗎?解釋為什麼。

可以。一個例子是隻讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。

4)一個指針可以是volatile 嗎?解釋為什麼。

可以。儘管這並不很常見。一個例子當中斷服務子程序修該一個指向一個buffer的指針時。

下面的函數有什麼錯誤:

int square(volatile int *ptr) {
return *ptr * *ptr;
}

下面是答案:

這段代碼有點變態。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由於*ptr指向一個volatile型參數,編譯器將產生類似下面的代碼:

int square(volatile int *ptr){
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}

由於*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:

long square(volatile int *ptr){
int a;
a = *ptr;
return a * a;
}

3.static const等等的用法,(能說出越多越好)(重點)

² 首先說說const的用法(絕對不能說是常數)

1)在定義的時候必須進行初始化

2)指針可以是const 指針,也可以是指向const對象的指針

3)定義為const的形參,即在函數內部是不能被修改的

4)類的成員函數可以被聲明為常成員函數,不能修改類的成員變量

5)類的成員函數可以返回的是常對象,即被const聲明的對象

6)類的成員變量是常成員變量不能在聲明時初始化,必須在構造函數的列表裡進行初始化

(注:千萬不要說const是個常數,會被認為是外行人的!!!!哪怕說個只讀也行)

下面的聲明都是什麼意思?

const int a; a是一個常整型數

int const a; a是一個常整型數

const int *a; a是一個指向常整型數的指針,整型數是不可修改的,但指針可以

int * const a; a為指向整型數的常指針,指針指向的整型數可以修改,但指針是不可修改的

int const * a const; a是一個指向常整型數的常指針,指針指向的整型數是不可修改的,同時指針也是不可修改的

通過給優化器一些附加的信息,使用關鍵字const也許能產生更緊湊的代碼。合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數,防止其被無意的代碼修改。簡而言之,這樣可以減少bug的出現。

Const如何做到只讀?

這些在編譯期間完成,對於內置類型,如int, 編譯器可能使用常數直接替換掉對此變量的引用。而對於結構體不一定。

c/c++linux後臺開發技術學習視頻,後臺私信【視頻】獲取

linux C/C++服務器後臺開發面試題總結(編程語言篇)

linux C/C++服務器後臺開發面試題總結(編程語言篇)

² 再說說static的用法(三個明顯的作用一定要答出來)

1)在函數體,一個被聲明為靜態的變量在這一函數被調用過程中維持其值不變。

2)在模塊內(但在函數體外),一個被聲明為靜態的變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問。它是一個本地的全局變量。

3)在模塊內,一個被聲明為靜態的函數只可被這一模塊內的其它函數調用。那就是,這個函數被限制在聲明它的模塊的本地範圍內使用

4)類內的static成員變量屬於整個類所擁有,不能在類內進行定義,只能在類的作用域內進行定義

5)類內的static成員函數屬於整個類所擁有,不能包含this指針,只能調用static成員函數

static全局變量與普通的全局變量有什麼區別?static局部變量和普通局部變量有什麼區別?static函數與普通函數有什麼區別?

static全局變量與普通的全局變量有什麼區別:static全局變量只初使化一次,防止在其他文件單元中被引用;

static局部變量和普通局部變量有什麼區別:static局部變量只被初始化一次,下一次依據上一次結果值;

static函數與普通函數有什麼區別:static函數在內存中只有一份,普通函數在每個被調用中維持一份拷貝

4.extern c 作用

告訴編譯器該段代碼以C語言進行編譯。

5.指針和引用的區別

1)引用是直接訪問,指針是間接訪問。

2)引用是變量的別名,本身不單獨分配自己的內存空間,而指針有自己的內存空間

3)引用綁定內存空間(必須賦初值),是一個變量別名不能更改綁定,可以改變對象的值。

總的來說:引用既具有指針的效率,又具有變量使用的方便性和直觀性

6. 關於靜態內存分配和動態內存分配的區別及過程

1) 靜態內存分配是在編譯時完成的,不佔用CPU資源;動態分配內存運行時完成,分配與釋放需要佔用CPU資源;

2)靜態內存分配是在棧上分配的,動態內存是堆上分配的;

3)動態內存分配需要指針或引用數據類型的支持,而靜態內存分配不需要;

4)靜態內存分配是按計劃分配,在編譯前確定內存塊的大小,動態內存分配運行時按需分配。

5)靜態分配內存是把內存的控制權交給了編譯器,動態內存把內存的控制權交給了程序員;

6)靜態分配內存的運行效率要比動態分配內存的效率要高,因為動態內存分配與釋放需要額外的開銷;動態內存管理水平嚴重依賴於程序員的水平,處理不當容易造成內存洩漏。

7. 頭文件中的 ifndef/define/endif 幹什麼用

預處理,防止頭文件被重複使用,包括pragma once都是這樣的

8. 宏定義求兩個元素的最小值

#define MIN(A,B) ((A) <= (B) ? (A) : (B))

9. 分別設置和清除一個整數的第三位?

 #define BIT3 (0x1<<3)
static int a;
void set_bit3(void){
a |= BIT3;
}
void clear_bit3(void){
a &= ~BIT3;
}

10. 用預處理指令#define 聲明一個常數,用以表明1年中有多少秒

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

11. 預處理器標識#error的目的是什麼?

拋出錯誤提示,標識外部宏是否被定義!

12. 嵌入式系統中經常要用到無限循環,你怎麼樣用C編寫死循環呢?

記住這是第一方案!!!!

while(1)

{

}

一些程序員更喜歡如下方案:

for(;;){

}

彙編語言的無線循環是:

Loop:

...

goto Loop;

13. 用變量a給出下面的定義

一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數並返回一個整型數 int (*a[10])(int);

14. 中斷是嵌入式系統中重要的組成部分,這導致了很多編譯開發商提供一種擴展—讓標準C支持中斷。具代表事實是,產生了一個新的關鍵字 __interrupt

16. memcpy函數的實現

void *memcpy(void *dest, const void *src, size_t count) {
char *tmp = dest;
const char *s = src;

while (count--)
*tmp++ = *s++;
return dest;
}

17. Strcpy函數實現

char *strcpy(char *dst,const char *src) { 
assert(dst != NULL && src != NULL);
char *ret = dst;
while((* dst++ = * src++) != '\\0') ;
return ret;
}

18. strcat函數的實現

char *strcat(char *strDes, const char *strSrc){
assert((strDes != NULL) && (strSrc != NULL));
char *address = strDes;
while (*strDes != ‘\\0′)
++ strDes;
while ((*strDes ++ = *strSrc ++) != ‘\\0′)
return address;
}

19.strncat實現

char *strncat(char *strDes, const char *strSrc, int count){
assert((strDes != NULL) && (strSrc != NULL));
char *address = strDes;
while (*strDes != ‘\\0′)
++ strDes;
while (count — && *strSrc != ‘\\0′ )
*strDes ++ = *strSrc ++;
*strDes = ‘\\0′;
return address;
}

20. strcmp函數實現

int strcmp(const char *str1,const char *str2){
/*不可用while(*str1++==*str2++)來比較,當不相等時仍會執行一次++,
return返回的比較值實際上是下一個字符。應將++放到循環體中進行。*/
while(*str1 == *str2){
if(*str1 == '\\0')
return0;

++str1;
++str2;
}
return *str1 - *str2;
}

21. strncmp實現

int strncmp(const char *s, const char *t, int count){

assert((s != NULL) && (t != NULL));

while (*s && *t && *s == *t && count –) {

++ s;

++ t;

}

return (*s – *t);

}

22.strlen函數實現

int strlen(const char *str){
assert(str != NULL);
int len = 0;
while (*str ++ != ‘\\0′)
++ len;
return len;
}

23. strpbrk函數實現

char * strpbrk(const char * cs,const char * ct){
const char *sc1,*sc2;
for( sc1 = cs; *sc1 != '\\0'; ++sc1){
for( sc2 = ct; *sc2 != '\\0'; ++sc2){
if (*sc1 == *sc2){
return (char *) sc1;
}
}
}
return NULL;
}

24. strstr函數實現

char *strstr(const char *s1,const char *s2){
int len2;
if(!(len2=strlen(s2)))//此種情況下s2不能指向空,否則strlen無法測出長度,這條語句錯誤
return(char*)s1;
for(;*s1;++s1)
{
if(*s1==*s2 && strncmp(s1,s2,len2)==0)
return(char*)s1;
}
return NULL;
}

25. string實現(注意:賦值構造,operator=是關鍵

class String{
public:
//普通構造函數
String(const char *str = NULL);
//拷貝構造函數
String(const String &other);
//賦值函數
String & operator=(String &other) ;
//析構函數
~String(void);
private:
char* m_str;
};

分別實現以上四個函數

//普通構造函數
String::String(const char* str){
if(str==NULL) //如果str為NULL,存空字符串{
m_str = new char[1]; //分配一個字節
*m_str = ‘\\0′; //賦一個’\\0′
}else{
str = new char[strlen(str) + 1];//分配空間容納str內容
strcpy(m_str, str); //複製str到私有成員m_str中
}
}

//析構函數
String::~String(){

if(m_str!=NULL) //如果m_str不為NULL,釋放堆內存{
delete [] m_str;
m_str = NULL;
}
}

//拷貝構造函數
String::String(const String &other){
m_str = new char[strlen(other.m_str)+1]; //分配空間容納str內容
strcpy(m_str, other.m_str); //複製other.m_str到私有成員m_str中
}

//賦值函數
String & String::operator=(String &other){
if(this == &other) //若對象與other是同一個對象,直接返回本{
return *this
}
delete [] m_str; //否則,先釋放當前對象堆內存
m_str = new char[strlen(other.m_str)+1]; //分配空間容納str內容
strcpy(m_str, other.m_str); //複製other.m_str到私有成員m_str中
return *this;
}

26. C語言同意一些令人震驚的結構,下面的結構是合法的嗎,如果是它做些什麼?

int a = 5, b = 7, c;

c = a+++b; 等同於 c = a++ + b;

因此, 這段代碼持行後a = 6, b = 7, c = 12。

27. 用struct關鍵字與class關鍵定義類以及繼承的區別

(1)定義類差別

struct關鍵字也可以實現類,用class和struct關鍵字定義類的唯一差別在於默認訪問級別:默認情況下,struct成員的訪問級別為public,而class成員的為private。語法使用也相同,直接將class改為struct即可。

(2)繼承差別

使用class保留字的派生類默認具有private繼承,而用struct保留字定義的類某人具有public繼承。其它則沒有任何區別。

主要點就兩個:默認的訪問級別和默認的繼承級別 class都是private

28.派生類與虛函數概述

(1) 派生類繼承的函數不能定義為虛函數。虛函數是希望派生類重新定義。如果派生類沒有重新定義某個虛函數,則在調用的時候會使用基類中定義的版本。

(2)派生類中函數的聲明必須與基類中定義的方式完全匹配。

(3) 基類中聲明為虛函數,則派生類也為虛函數。

29. 虛函數與純虛函數區別

1)虛函數在子類裡面也可以不重載的;但純虛必須在子類去實現

2)帶純虛函數的類叫虛基類也叫抽象類,這種基類不能直接生成對象,只能被繼承,重寫虛函數後才能使用,運行時動態動態綁定!

30.深拷貝與淺拷貝

淺拷貝:

char ori[]=“hello”;char *copy=ori;

深拷貝:

char ori[]="hello"; char *copy=new char[]; copy=ori;

淺拷貝只是對指針的拷貝,拷貝後兩個指針指向同一個內存空間,深拷貝不但對指針進行拷貝,而且對指針指向的內容進行拷貝,經深拷貝後的指針是指向兩個不同地址的指針。

淺拷貝可能出現的問題:

1) 淺拷貝只是拷貝了指針,使得兩個指針指向同一個地址,這樣在對象塊結束,調用函數析構的時,會造成同一份資源析構2次,即delete同一塊內存2次,造成程序崩潰。

2) 淺拷貝使得兩個指針都指向同一塊內存,任何一方的變動都會影響到另一方。

3) 同一個空間,第二次釋放失敗,導致無法操作該空間,造成內存洩漏。

31. stl各容器的實現原理(必考)

1) Vector順序容器,是一個動態數組,支持隨機插入、刪除、查找等操作,在內存中是一塊連續的空間。在原有空間不夠情況下自動分配空間,增加為原來的兩倍。vector隨機存取效率高,但是在vector插入元素,需要移動的數目多,效率低下。

注:vector動態增加大小時是以原大小的兩倍另外配置一塊較大的空間,然後將原內容拷貝過來,然後才開始在原內容之後構造新元素,並釋放原空間。因此,對vector空間重新配置,指向原vector的所有迭代器就都失效了。

2) Map關聯容器,以鍵值對的形式進行存儲,方便進行查找。關鍵詞起到索引的作用,值則表示與索引相關聯的數據。紅黑樹的結構實現,插入刪除等操作都在O(logn)時間內完成。

3) Set是關聯容器,set每個元素只包含一個關鍵字。set支持高效的關鍵字檢查是否在set中。set也是以紅黑樹的結構實現,支持高效插入、刪除等操作。

32.哪些庫函數屬於高危函數,為什麼?

strcpy 賦值到目標區間可能會造成緩衝區溢出!

33.STL有7種主要容器:vector,list,deque,map,multimap,set,multiset

34.你如何理解MVC。簡單舉例來說明其應用。

MVC模式是observer 模式的一個特例,現在很多都是java的一些框架,MFC的,PHP的。

35.C++特點是什麼,多態實現機制?(面試問過)多態作用?兩個必要條件?

C++中多態機制主要體現在兩個方面,一個是函數的重載,一個是接口的重寫。接口多態指的是“一個接口多種形態”。每一個對象內部都有一個虛表指針,該虛表指針被初始化為本類的虛表。所以在程序中,不管你的對象類型如何轉換,但該對象內部的虛表指針是固定的,所以呢,才能實現動態的對象函數調用,這就是C++多態性實現的原理。

多態的基礎是繼承,需要虛函數的支持,簡單的多態是很簡單的。子類繼承父類大部分的資源,不能繼承的有構造函數,析構函數,拷貝構造函數,operator=函數,友元函數等等

作用:

  1. 隱藏實現細節,代碼能夠模塊化;2. 接口重用:為了類在繼承和派生的時候正確調用。

必要條件:

1. 一個基類的指針或者引用指向派生類的對象;2.虛函數

36. 多重繼承有什麼問題? 怎樣消除多重繼承中的二義性?

1)增加程序的複雜度,使程序的編寫和維護比較困難,容易出錯;

2)繼承類和基類的同名函數產生了二義性,同名函數不知道調用基類還是繼承類,C++中使用虛函數解決這個問題

3)繼承過程中可能會繼承一些不必要的數據,對於多級繼承,可能會產生數據很長

可以使用成員限定符和虛函數解決多重繼承中函數的二義性問題。

37.求兩個數的乘積和商數,該作用由宏定義來實現

#define product(a,b) ((a)*(b))

#define divide(a,b) ((a)/(b))

38.什麼叫靜態關聯,什麼叫動態關聯

多態中,靜態關聯是程序在編譯階段就能確定實際執行動作,程序運行才能確定叫動態關聯

39.什麼叫智能指針?常用的智能指針有哪些?智能指針的實現?

智能指針是一個存儲指向動態分配(堆)對象指針的類,構造函數傳入普通指針,析構函數釋放指針。棧上分配,函數或程序結束自動釋放,防止內存洩露。使用引用計數器,類與指向的對象相關聯,引用計數跟蹤該類有多少個對象共享同一指針。創建類的新對象時,初始化指針並將引用計數置為1;當對象作為另一對象的副本而創建,增加引用計數;對一個對象進行賦值時,減少引用計數,並增加右操作數所指對象的引用計數;調用析構函數時,構造函數減少引用計數,當引用計數減至0,則刪除基礎對象。

std::auto_ptr,不支持複製(拷貝構造函數)和賦值(operator =),編譯不會提示出錯。

C++11引入的unique_ptr, 也不支持複製和賦值,但比auto_ptr好,直接賦值會編譯出錯。

C++11或boost的shared_ptr,基於引用計數的智能指針。可隨意賦值,直到內存的引用計數為0的時候這個內存會被釋放。還有Weak_ptr

40.枚舉與#define 宏的區別

1)#define 宏常量是在預編譯階段進行簡單替換。枚舉常量則是在編譯的時候確定其值。

2)可以調試枚舉常量,但是不能調試宏常量。

3)枚舉可以一次定義大量相關的常量,而#define 宏一次只能定義一個。

41.介紹一下函數的重載

重載是在不同類型上作不同運算而又用同樣的名字的函數。重載函數至少在參數個數,參數類型, 或參數順序上有所不同。

42.派生新類的過程要經歷三個步驟

1.吸收基類成員 2.改造基類成員 3.添加新成員

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

1)封裝:將客觀事物抽象成類,每個類對自身的數據和方法實行2)繼承3)多態:允許一個基類的指針或引用指向一個派生類對象

44.多態性體現都有哪些?動態綁定怎麼實現?

多態性是一個接口,多種實現,是面向對象的核心。 編譯時多態性:通過重載函數實現。運行時多態性:通過虛函數實現,結合動態綁定。

45.虛函數,虛函數表裡面內存如何分配?

編譯時若基類中有虛函數,編譯器為該的類創建一個一維數組的虛表,存放是每個虛函數的地址。基類和派生類都包含虛函數時,這兩個類都建立一個虛表。構造函數中進行虛表的創建和虛表指針的初始化。在構造子類對象時,要先調用父類的構造函數,初始化父類對象的虛表指針,該虛表指針指向父類的虛表。執行子類的構造函數時,子類對象的虛表指針被初始化,指向自身的虛表。每一個類都有虛表。虛表可以繼承,如果子類沒有重寫虛函數,那麼子類虛表中仍然會有該函數的地址,只不過這個地址指向的是基類的虛函數實現。派生類的虛表中虛函數地址的排列順序和基類的虛表中虛函數地址排列順序相同。當用一個指針/引用調用一個函數的時候,被調用的函數是取決於這個指針/引用的類型。即如果這個指針/引用是基類對象的指針/引用就調用基類的方法;如果指針/引用是派生類對象的指針/引用就調用派生類的方法,當然如果派生類中沒有此方法,就會向上到基類裡面去尋找相應的方法。這些調用在編譯階段就確定了。當涉及到多態性的時候,採用了虛函數和動態綁定,此時的調用就不會在編譯時候確定而是在運行時確定。不在單獨考慮指針/引用的類型而是看指針/引用的對象的類型來判斷函數的調用,根據對象中虛指針指向的虛表中的函數的地址來確定調用哪個函數。

46. 純虛函數如何定義?含有純虛函數的類稱為什麼?為什麼析構函數要定義成虛函數?

純虛函數是在基類中聲明的虛函數,它在基類中沒有定義,但要求任何派生類都要定義自己的實現方法。純虛函數是虛函數再加上= 0。virtual void fun ()=0。含有純虛函數的類稱為抽象類在很多情況下,基類本身生成對象是不合情理的。例如,動物作為一個基類可以派生出老虎、孔雀等子類,但動物本身生成對象明顯不合常理。同時含有純虛擬函數的類稱為抽象類,它不能生成對象。如果析構函數不是虛函數,那麼釋放內存時候,編譯器會使用靜態聯編,認為p就是一個基類指針,調用基類析構函數,這樣子類對象的內存沒有釋放,造成內存洩漏。定義成虛函數以後,就會動態聯編,先調用子類析構函數,再基類。

47. C++中哪些不能是虛函數?

1)普通函數只能重載,不能被重寫,因此編譯器會在編譯時綁定函數。

2)構造函數是知道全部信息才能創建對象,然而虛函數允許只知道部分信息。

3)內聯函數在編譯時被展開,虛函數在運行時才能動態綁定函數。

4)友元函數 因為不可以被繼承。

5)靜態成員函數 只有一個實體,不能被繼承。父類和子類共有。

48. 類型轉換有哪些?各適用什麼環境?dynamic_cast轉換失敗時,會出現什麼情況(對指針,返回NULL.對引用,拋出bad_cast異常)?

靜態類型轉換,static_cast,基本類型之間和具有繼承關係的類型。

例子A,double類型轉換成int。B,將子類對象轉換成基類對象。

常量類型轉換,const_cast, 去除指針變量的常量屬性。

無法將非指針的常量轉換為普通變量。

動態類型轉換,dynamic_cast,運行時進行轉換分析的,並非在編譯時進行。dynamic_cast轉換符只能用於含有虛函數的類。dynamic_cast用於類層次間的向上轉換和向下轉換,還可以用於類間的交叉轉換。在類層次間進行向上轉換,即子類轉換為父類,此時完成的功能和static_cast是相同的,因為編譯器默認向上轉換總是安全的。向下轉換時,dynamic_cast具有類型檢查的功能,更加安全。類間的交叉轉換指的是子類的多個父類之間指針或引用的轉換。該函數只能在繼承類對象的指針之間或引用之間進行類型轉換,或者有虛函數的類。

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

#ifdef __cplusplus
cout<<"C++";
#else
cout<<"c";
#endif

50. 為什麼要用static_cast轉換而不用c語言中的轉換?

Static_cast轉換,它會檢查類型看是否能轉換,有類型安全檢查。

比如,這個在C++中合法,但是確實錯誤的。

A* a= new A;

B* b = (B*)a;

51. 操作符重載(+操作符),具體如何去定義?

除了類屬關係運算符”.”、成員指針運算符”.*”、作用域運算符”::”、sizeof運算符和三目運算符”?:”以外,C++中的所有運算符都可以重載。

<返回類型說明符> operator <運算符符號>(<參數表>){}

重載為類的成員函數和重載為類的非成員函數。參數個數會不同,應為this指針。

52. 內存對齊的原則?

A.結構體的大小為最大成員的整數倍。

B.成員首地址的偏移量為其類型大小整數倍。

53. 內聯函數與宏定義的區別?

內聯函數是用來消除函數調用時的時間開銷。頻繁被調用的短小函數非常受益。

A. 宏定義不檢查函數參數,返回值什麼的,只是展開,相對來說,內聯函數會檢查參數類型,所以更安全。

B. 宏是由預處理器對宏進行替代,而內聯函數是通過編譯器控制來實現的

54. 動態分配對象和靜態分配對象的區別?

動態分配就是用運算符new來創建一個類的對象,在堆上分配內存。

靜態分配就是A a;這樣來由編譯器來創建一個對象,在棧上分配內存。

55. explicit是幹什麼用的 ?

構造器 ,可以阻止不應該允許的經過轉換構造函數進行的隱式轉換的發生。explicit是用來防止外部非正規的拷貝構造的,要想不存在傳值的隱式轉換問題。

56. 內存溢出有那些因素?

(1) 使用非類型安全(non-type-safe)的語言如 C/C++ 等。

(2) 以不可靠的方式存取或者複製內存緩衝區。

(3) 編譯器設置的內存緩衝區太靠近關鍵數據結構。

57. new與malloc的區別,delete和free的區別?

1.malloc/free是C/C++語言的標準庫函數,new/delete是C++的運算符

2.new能夠自動分配空間大小,malloc傳入參數。

3. new/delete能進行對對象進行構造和析構函數的調用進而對內存進行更加詳細的工作,而malloc/free不能。

既然new/delete的功能完全覆蓋了malloc/free,為什麼C++還保留malloc/free呢?因為C++程序經常要調用C函數,而C程序只能用malloc/free管理動態內存。

58. 必須使用初始化列表初始化數據成員的情況

1.是對象的情況;

2.const修飾的類成員;

3.引用成員數據;

類成員變量的初始化不是按照初始化表順序被初始化,是按照在類中聲明的順序被初始化的。

59.深入談談堆和棧

1).分配和管理方式不同 :

堆是動態分配的,其空間的分配和釋放都由程序員控制。

棧由編譯器自動管理。棧有兩種分配方式:靜態分配和動態分配。靜態分配由編譯器完成,比如局部變量的分配。動態分配由alloca()函數進行分配,但是棧的動態分配和堆是不同的,它的動態分配是由編譯器進行釋放,無須手工控制。

2).產生碎片不同

對堆來說,頻繁的new/delete或者malloc/free勢必會造成內存空間的不連續,造成大量的碎片,使程序效率降低。

對棧而言,則不存在碎片問題,因為棧是先進後出的隊列,永遠不可能有一個內存塊從棧中間彈出。

3).生長方向不同

堆是向著內存地址增加的方向增長的,從內存的低地址向高地址方向增長。

棧是向著內存地址減小的方向增長,由內存的高地址向低地址方向增長。

60.內存的靜態分配和動態分配的區別?

時間不同。靜態分配發生在程序編譯和連接時。動態分配則發生在程序調入和執行時。

空間不同。堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,比如局部變量的分配。alloca,可以從棧裡動態分配內存,不用擔心內存洩露問題,當函數返回時,通過alloca申請的內存就會被自動釋放掉。

61. 模版怎麼實現?模版作用?

實現:template void swap(T& a, T& b){}

作用:將算法與具體對象分離,與類型無關,通用,節省精力

62. 多重類構造和析構的順序

記住析構函數的調用順序與構造函數是相反的。

63. 迭代器刪除元素的會發生什麼?

迭代器失效

64. 靜態成員函數和數據成員有什麼意義?

1)非靜態數據成員,每個對象都有自己的拷貝。而靜態數據成員被當作是類的成員,是該類的所有對象所共有的,在程序中只分配一次內存只有一份拷貝,所以對象都共享,值對每個對象都是一樣的,它的值可以更新。

2)靜態數據成員存儲在全局數據區,所以不能在類聲明中定義,應該在類外定義。由於它不屬於特定的類對象,在沒有產生類對象時作用域就可見,即在沒有產生類的實例時,我們就可以操作它。

3)靜態成員函數與靜態數據成員一樣,都是在類的內部實現,屬於類定義的一部分。因為普通成員函數總是具體的屬於具體對象的,每個有this指針。靜態成員函數沒有this指針,它無法訪問屬於類對象的非靜態數據成員,也無法訪問非靜態成員函數。靜態成員之間可以互相訪問,包括靜態成員函數訪問靜態數據成員和訪問靜態成員函數;

4)非靜態成員函數可以任意地訪問靜態成員函數和靜態數據成員;

5)沒有this指針的額外開銷,靜態成員函數與類的全局函數相比,速度上會有少許的增長;

6)調用靜態成員函數,可以用成員訪問操作符(.)和(->)為一個類的對象或指向類對象的指調用靜態成員函數。

65.sizeof一個類求大小(注意成員變量,函數,虛函數,繼承等等對大小的影響)

http://blog.csdn.net/jollyhope/article/details/1895357

http://www.cnblogs.com/BeyondTechnology/archive/2010/09/21/1832369.html

66請用C/C++實現字符串反轉(不調用庫函數)”abc”類型的

char *reverse_str(char *str) {
if(NULL == str) { //字符串為空直接返回
return str;
}
char *begin;
char *end;
begin = end = str;

while(*end != '\\0') { //end指向字符串的末尾
end++;
}
--end;

char temp;
while(begin < end) { //交換兩個字符
temp = *begin;
*begin = *end;
*end = temp;
begin++;
end--;
}
return str; //返回結果
}

67.寫一個函數,將字符串翻轉,翻轉方式如下:“I am a student”反轉成“student a am I”,不借助任何庫函數

 1 #include "stdio.h"
2 #include <iostream>
3 using namespace std;
4
5 void revesal(char * start, char* end){
6 char *temp_s = start;
7 char *temp_e = end;
8 while(temp_s < temp_e){
9 char temp= *temp_s;
10 *temp_s= *temp_e;
11 *temp_e = temp;
12 ++temp_s;
13 --temp_e;
14 }
15 return;
16 }
17
18 void revesal_str(char *str){
19 if(str == NULL){
20 return;
21 }
22
23 char *start = str;
24 char *end = str;
25
26 while(*++end !='\\0');
27 revesal(start, end-1);
28 cout << str << endl;
29 char *sub_start = str;
30 while(start < end + 1 ){
31 if(*start == ' ' || *start == '\\0'){
32 char *temp = start - 1;
33 revesal(sub_start,temp);
34 while(*++start ==' ');
35 sub_start = start;
36 continue;
37 }
38 ++start;
39 }
40 }

68.析構函數可以拋出異常嗎?為什麼不能拋出異常?除了資源洩露,還有其他需考慮的因素嗎?

C++標準指明析構函數不能、也不應該拋出異常。C++異常處理模型最大的特點和優勢就是對C++中的面向對象提供了最強大的無縫支持。那麼如果對象在運行期間出現了異常,C++異常處理模型有責任清除那些由於出現異常所導致的已經失效了的對象(也即對象超出了它原來的作用域),並釋放對象原來所分配的資源, 這就是調用這些對象的析構函數來完成釋放資源的任務,所以從這個意義上說,析構函數已經變成了異常處理的一部分。

1)如果析構函數拋出異常,則異常點之後的程序不會執行,如果析構函數在異常點之後執行了某些必要的動作比如釋放某些資源,則這些動作不會執行,會造成諸如資源洩漏的問題。

2)通常異常發生時,c++的機制會調用已經構造對象的析構函數來釋放資源,此時若析構函數本身也拋出異常,則前一個異常尚未處理,又有新的異常,會造成程序崩潰的問題。

69. 拷貝構造函數作用及用途?什麼時候需要自定義拷貝構造函數?

一般如果構造函數中存在動態內存分配,則必須定義拷貝構造函數。否則,可能會導致兩個對象成員指向同一地址,出現“指針懸掛問題”。

70. 100萬個32位整數,如何最快找到中位數。能保證每個數是唯一的,如何實現O(N)算法?

1).內存足夠時:快排

2).內存不足時:分桶法:化大為小,把所有數劃分到各個小區間,把每個數映射到對應的區間裡,對每個區間中數的個數進行計數,數一遍各個區間,看看中位數落在哪個區間,若夠小,使用基於內存的算法,否則 繼續劃分

71. OFFSETOF(s, m)的宏定義,s是結構類型,m是s的成員,求m在s中的偏移量。

#define OFFSETOF(s, m) size_t(&((s*)0)->m)

72. C++虛函數是如何實現的?

使用虛函數表。 C++對象使用虛表, 如果是基類的實例,對應位置存放的是基類的函數指針;如果是繼承類,對應位置存放的是繼承類的函數指針(如果在繼承類有實現)。所以 ,當使用基類指針調用對象方法時,也會根據具體的實例,調用到繼承類的方法。

73. C++的虛函數有什麼作用?

虛函數作用是實現多態,虛函數其實是實現封裝,使得使用者不需要關心實現的細節。在很多設計模式中都是這樣用法,例如Factory、Bridge、Strategy模式。

74.MFC中CString是類型安全類嗎,為什麼?

不是,其他數據類型轉換到CString可以使用CString的成員函數Format來轉換

74.動態鏈接庫的兩種使用方法及特點?

1).載入時動態鏈接,模塊非常明確調用某個導出函數,使得他們就像本地函數一樣。這需要鏈接時鏈接那些函數所在DLL的導入庫,導入庫向系統提供了載入DLL時所需的信息及DLL函數定位。

2)運行時動態鏈接。

"

相關推薦

推薦中...