www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > 公眾號精選 > C語言與CPP編程
[導(dǎo)讀]變量的聲明和定義有什么區(qū)別?

1 變量的聲明和定義有什么區(qū)別

變量的定義為變量分配地址和存儲空間, 變量的聲明不分配地址。一個變量可以在多個地方聲明, 但是只在一個地方定義。加入extern 修飾的是變量的聲明,說明此變量將在文件以外或在文件后面部分定義。

說明:很多時候一個變量,只是聲明不分配內(nèi)存空間,直到具體使用時才初始化,分配內(nèi)存空間, 如外部變量。

int main() 
{
   extern int A;
   //這是個聲明而不是定義,聲明A是一個已經(jīng)定義了的外部變量
   //注意:聲明外部變量時可以把變量類型去掉如:extern A;
   dosth(); //執(zhí)行函數(shù)
}
int A; //是定義,定義了A為整型的外部變量

2 簡述#ifdef、#else、#endif和#ifndef的作用

利用#ifdef、#endif將某程序功能模塊包括進去,以向特定用戶提供該功能。在不需要時用戶可輕易將其屏蔽。

#ifdef MATH #include "math.c" #endif 

在子程序前加上標記,以便于追蹤和調(diào)試。

#ifdef DEBUG printf ("Indebugging......!"); #endif 

應(yīng)對硬件的限制。由于一些具體應(yīng)用環(huán)境的硬件不一樣,限于條件,本地缺乏這種設(shè)備,只能繞過硬件,直接寫出預(yù)期結(jié)果。

「注意」:雖然不用條件編譯命令而直接用if語句也能達到要求,但那樣做目標程序長(因為所有語句都編譯),運行時間長(因為在程序運行時間對if語句進行測試)。而采用條件編譯,可以減少被編譯的語句,從而減少目標程序的長度,減少運行時間。

3 寫出int 、bool、 float 、指針變量與 “零值”比較的if 語句

//int與零值比較 if ( n == 0 ) if ( n != 0 )
 
//bool與零值比較 if (flag) //   表示flag為真 if (!flag) //   表示flag為假 
 
//float與零值比較 
const float EPSINON = 0.00001; if ((x >= - EPSINON) && (x <= EPSINON) //其中EPSINON是允許的誤差(即精度)。 //指針變量與零值比較 if (p == NULL) if (p != NULL)

4 結(jié)構(gòu)體可以直接賦值嗎

聲明時可以直接初始化,同一結(jié)構(gòu)體的不同對象之間也可以直接賦值,但是當結(jié)構(gòu)體中含有指針“成員”時一定要小心。

「注意」:當有多個指針指向同一段內(nèi)存時,某個指針釋放這段內(nèi)存可能會導(dǎo)致其他指針的非法操作。因此在釋放前一定要確保其他指針不再使用這段內(nèi)存空間。

5 sizeof 和strlen 的區(qū)別

  • sizeof是一個操作符,strlen是庫函數(shù)。
  • sizeof的參數(shù)可以是數(shù)據(jù)的類型,也可以是變量,而strlen只能以結(jié)尾為‘\0’的字符串作參數(shù)。
  • 編譯器在編譯時就計算出了sizeof的結(jié)果,而strlen函數(shù)必須在運行時才能計算出來。并且sizeof計算的是數(shù)據(jù)類型占內(nèi)存的大小,而strlen計算的是字符串實際的長度。
  • 數(shù)組做sizeof的參數(shù)不退化,傳遞給strlen就退化為指針了

6 C 語言的關(guān)鍵字 static 和 C++ 的關(guān)鍵字 static 有什么區(qū)別

在 C 中 static 用來修飾局部靜態(tài)變量和外部靜態(tài)變量、函數(shù)。而 C++中除了上述功能外,還用來定義類的成員變量和函數(shù)。即靜態(tài)成員和靜態(tài)成員函數(shù)。

「注意」:編程時 static 的記憶性,和全局性的特點可以讓在不同時期調(diào)用的函數(shù)進行通信,傳遞信息,而 C++的靜態(tài)成員則可以在多個對象實例間進行通信,傳遞信息。

7 C 語言的 malloc 和 C++ 中的 new 有什么區(qū)別

  • new 、delete 是操作符,可以重載,只能在C++ 中使用。
  • malloc、free 是函數(shù),可以覆蓋,C、C++ 中都可以使用。
  • new 可以調(diào)用對象的構(gòu)造函數(shù),對應(yīng)的delete 調(diào)用相應(yīng)的析構(gòu)函數(shù)。
  • malloc 僅僅分配內(nèi)存,free 僅僅回收內(nèi)存,并不執(zhí)行構(gòu)造和析構(gòu)函數(shù)
  • new 、delete 返回的是某種數(shù)據(jù)類型指針,malloc、free 返回的是void 指針。

「注意」:malloc 申請的內(nèi)存空間要用free 釋放,而new 申請的內(nèi)存空間要用delete 釋放,不要混用。

8 寫一個 “標準”宏MIN

#define min(a,b)((a)<=(b)?(a):(b))

9 ++i和i++的區(qū)別

++i先自增1,再返回,i++先返回i,再自增1

10 volatile有什么作用

  • 狀態(tài)寄存器一類的并行設(shè)備硬件寄存器。
  • 一個中斷服務(wù)子程序會訪問到的非自動變量。
  • 多線程間被幾個任務(wù)共享的變量。

「注意」:雖然volatile在嵌入式方面應(yīng)用比較多,但是在PC軟件的多線程中,volatile修飾的臨界變量也是非常實用的。

11 一個參數(shù)可以既是const又是volatile嗎

可以,用const和volatile同時修飾變量,表示這個變量在程序內(nèi)部是只讀的,不能改變的,只在程序外部條件變化下改變,并且編譯器不會優(yōu)化這個變量。每次使用這個變量時,都要小心地去內(nèi)存讀取這個變量的值,而不是去寄存器讀取它的備份。

注意:在此一定要注意const的意思,const只是不允許程序中的代碼改變某一變量,其在編譯期發(fā)揮作用,它并沒有實際地禁止某段內(nèi)存的讀寫特性。

12 a 和&a 有什么區(qū)別

&a:其含義就是“變量a的地址”。

*a:用在不同的地方,含義也不一樣。

  • 在聲明語句中,*a只說明a是一個指針變量,如int *a;
  • 在其他語句中,*a前面沒有操作數(shù)且a是一個指針時,*a代表指針a指向的地址內(nèi)存放的數(shù)據(jù),如b=*a;
  • *a前面有操作數(shù)且a是一個普通變量時,a代表乘以a,如c=ba。

13 用C 編寫一個死循環(huán)程序

while(1) 
{ }

「注意」:很多種途徑都可實現(xiàn)同一種功能,但是不同的方法時間和空間占用度不同,特別是對于嵌入 式軟件,處理器速度比較慢,存儲空間較小,所以時間和空間優(yōu)勢是選擇各種方法的首要考慮條件。

14 結(jié)構(gòu)體內(nèi)存對齊問題

請寫出以下代碼的輸出結(jié)果:

#include struct S1
{
    int i:8;
    char j:4;
    int a:4;
    double b;
};
 
struct S2
{
    int i:8;
    char j:4;
    double b;
    int a:4;
};
 
struct S3
{
    int i;
    char j;
    double b;
    int a;
};
 
int main()
{ printf("%d\n",sizeof(S1));  // 輸出8 printf("%d\n",sizeof(S1);  // 輸出12 printf("%d\n",sizeof(Test3));  // 輸出8 return 0;
}
 
sizeof(S1)=16
sizeof(S2)=24
sizeof(S3)=32

「說明」:結(jié)構(gòu)體作為一種復(fù)合數(shù)據(jù)類型,其構(gòu)成元素既可以是基本數(shù)據(jù)類型的變量,也可以是一些復(fù)合型類型數(shù)據(jù)。對此,編譯器會自動進行成員變量的對齊以提高運算效率。默認情況下,按自然對齊條件分配空間。各個成員按照它們被聲明的順序在內(nèi)存中順序存儲,第一個成員的地址和整個結(jié)構(gòu)的地址相同,向結(jié)構(gòu)體成員中size最大的成員對齊。

許多實際的計算機系統(tǒng)對基本類型數(shù)據(jù)在內(nèi)存中存放的位置有限制,它們會要求這些數(shù)據(jù)的首地址的值是某個數(shù)k(通常它為4或8)的倍數(shù),而這個k則被稱為該數(shù)據(jù)類型的對齊模數(shù)。

15 全局變量和局部變量有什么區(qū)別?實怎么實現(xiàn)的?操作系統(tǒng)和編譯器是怎么知道的?

  • 全局變量是整個程序都可訪問的變量,誰都可以訪問,生存期在整個程序從運行到結(jié)束(在程序結(jié)束時所占內(nèi)存釋放);
  • 而局部變量存在于模塊(子程序,函數(shù))中,只有所在模塊可以訪問,其他模塊不可直接訪問,模塊結(jié)束(函數(shù)調(diào)用完畢),局部變量消失,所占據(jù)的內(nèi)存釋放。
  • 操作系統(tǒng)和編譯器,可能是通過內(nèi)存分配的位置來知道的,全局變量分配在全局數(shù)據(jù)段并且在程序開始運行的時候被加載.局部變量則分配在堆棧里面。

16 簡述C、C++程序編譯的內(nèi)存分配情況

  • 從靜態(tài)存儲區(qū)域分配:

內(nèi)存在程序編譯時就已經(jīng)分配好,這塊內(nèi)存在程序的整個運行期間都存在。速度快、不容易出錯, 因為有系統(tǒng)會善后。例如全局變量,static 變量,常量字符串等。

  • 在棧上分配:

在執(zhí)行函數(shù)時,函數(shù)內(nèi)局部變量的存儲單元都在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時這些存儲單元自動被釋 放。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。大小為2M。

  • 從堆上分配:

即動態(tài)內(nèi)存分配。程序在運行的時候用 malloc 或new 申請任意大小的內(nèi)存,程序員自己負責在何 時用free 或delete 釋放內(nèi)存。動態(tài)內(nèi)存的生存期由程序員決定,使用非常靈活。如果在堆上分配了空間,就有責任回收它,否則運行的程序會出現(xiàn)內(nèi)存泄漏,另外頻繁地分配和釋放不同大小的堆空間將會產(chǎn)生 堆內(nèi)碎塊。

一個C、C++程序編譯時內(nèi)存分為5 大存儲區(qū):堆區(qū)、棧區(qū)、全局區(qū)、文字常量區(qū)、程序代碼區(qū)。

17 簡述strcpy、sprintf 與memcpy 的區(qū)別

  • 操作對象不同,strcpy 的兩個操作對象均為字符串,sprintf 的操作源對象可以是多種數(shù)據(jù)類型, 目的操作對象是字符串,memcpy 的兩個對象就是兩個任意可操作的內(nèi)存地址,并不限于何種數(shù)據(jù)類型。
  • 執(zhí)行效率不同,memcpy 最高,strcpy 次之,sprintf 的效率最低。
  • 實現(xiàn)功能不同,strcpy 主要實現(xiàn)字符串變量間的拷貝,sprintf 主要實現(xiàn)其他數(shù)據(jù)類型格式到字 符串的轉(zhuǎn)化,memcpy 主要是內(nèi)存塊間的拷貝。

「注意」:strcpy、sprintf 與memcpy 都可以實現(xiàn)拷貝的功能,但是針對的對象不同,根據(jù)實際需求,來 選擇合適的函數(shù)實現(xiàn)拷貝功能。

18 請解析((void ()( ) )0)( )的含義

  • void (*0)( ) :是一個返回值為void,參數(shù)為空的函數(shù)指針0。
  • (void (*)( ))0:把0轉(zhuǎn)變成一個返回值為void,參數(shù)為空的函數(shù)指針。
  • (void ()( ))0:在上句的基礎(chǔ)上加*表示整個是一個返回值為void,無參數(shù),并且起始地址為0的函數(shù)的名字。
  • ( (void ()( ))0)( ):這就是上句的函數(shù)名所對應(yīng)的函數(shù)的調(diào)用。

19 C語言的指針和引用和c++的有什么區(qū)別?

  • 指針有自己的一塊空間,而引用只是一個別名;
  • 使用sizeof看一個指針的大小是4,而引用則是被引用對象的大?。?/span>
  • 作為參數(shù)傳遞時,指針需要被解引用才可以對對象進行操作,而直接對引 用的修改都會改變引用所指向的對象;
  • 可以有const指針,但是沒有const引用;
  • 指針在使用中可以指向其它對象,但是引用只能是一個對象的引用,不能 被改變;
  • 指針可以有多級指針(**p),而引用止于一級;
  • 指針和引用使用++運算符的意義不一樣;
  • 如果返回動態(tài)內(nèi)存分配的對象或者內(nèi)存,必須使用指針,引用可能引起內(nèi)存泄露。

20 typedef 和define 有什么區(qū)別

  • 用法不同:typedef 用來定義一種數(shù)據(jù)類型的別名,增強程序的可讀性。define 主要用來定義 常量,以及書寫復(fù)雜使用頻繁的宏。
  • 執(zhí)行時間不同:typedef 是編譯過程的一部分,有類型檢查的功能。define 是宏定義,是預(yù)編譯的部分,其發(fā)生在編譯之前,只是簡單的進行字符串的替換,不進行類型的檢查。
  • 作用域不同:typedef 有作用域限定。define 不受作用域約束,只要是在define 聲明后的引用 都是正確的。
  • 對指針的操作不同:typedef 和define 定義的指針時有很大的區(qū)別。

「注意」:typedef 定義是語句,因為句尾要加上分號。而define 不是語句,千萬不能在句尾加分號。

21 指針常量與常量指針區(qū)別

指針常量是指定義了一個指針,這個指針的值只能在定義時初始化,其他地方不能改變。常量指針 是指定義了一個指針,這個指針指向一個只讀的對象,不能通過常量指針來改變這個對象的值。指針常量強調(diào)的是指針的不可改變性,而常量指針強調(diào)的是指針對其所指對象的不可改變性。

「注意」:無論是指針常量還是常量指針,其最大的用途就是作為函數(shù)的形式參數(shù),保證實參在被調(diào)用 函數(shù)中的不可改變特性。

22 簡述隊列和棧的異同

隊列和棧都是線性存儲結(jié)構(gòu),但是兩者的插入和刪除數(shù)據(jù)的操作不同,隊列是“先進先出”,棧是 “后進先出”。

「注意」:區(qū)別棧區(qū)和堆區(qū)。堆區(qū)的存取是“順序隨意”,而棧區(qū)是“后進先出”。棧由編譯器自動分 配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。堆一般由程序員 分配釋放, 若程序員不釋放,程序結(jié)束時可能由OS 回收。分配方式類似于鏈表。它與本題中的堆和棧是兩回事。堆棧只是一種數(shù)據(jù)結(jié)構(gòu),而堆區(qū)和棧區(qū)是程序的不同內(nèi)存存儲區(qū)域。

23 設(shè)置地址為0x67a9 的整型變量的值為0xaa66

int *ptr; 
ptr = (int *)0x67a9; 
*ptr = 0xaa66;

「注意」:這道題就是強制類型轉(zhuǎn)換的典型例子,無論在什么平臺地址長度和整型數(shù)據(jù)的長度是一樣的, 即一個整型數(shù)據(jù)可以強制轉(zhuǎn)換成地址指針類型,只要有意義即可。

24 編碼實現(xiàn)字符串轉(zhuǎn)化為數(shù)字

編碼實現(xiàn)函數(shù)atoi(),設(shè)計一個程序,把一個字符串轉(zhuǎn)化為一個整型數(shù)值。例如數(shù)字:“5486321 ”, 轉(zhuǎn)化成字符:5486321。

int myAtoi(const char * str) 
{
   int num = 0; //保存轉(zhuǎn)換后的數(shù)值 
   int isNegative = 0; //記錄字符串中是否有負號 
 
   int n =0; 
   char *p = str; if(p == NULL) //判斷指針的合法性 
   { return -1; 
   } while(*p++ != '\0') //計算數(shù)字符串度 
   { 
      n++; 
   } 
   p = str; if(p[0] == '-') //判斷數(shù)組是否有負號 
   { 
      isNegative = 1; 
   } 
 
   char temp = '0'; for(int i = 0 ; i < n; i++) { char temp = *p++; if(temp > '9' ||temp < '0') //濾除非數(shù)字字符 
      { continue; 
      } if(num !=0 || temp != '0') //濾除字符串開始的0 字符 
      { 
         temp -= 0x30; //將數(shù)字字符轉(zhuǎn)換為數(shù)值 
          num += temp *int( pow(10 , n - 1 -i) ); 
       } 
   } if(isNegative) //如果字符串中有負號,將數(shù)值取反 
   { return (0 - num); 
   } else { return num; //返回轉(zhuǎn)換后的數(shù)值 
   } 
}

25 C語言的結(jié)構(gòu)體和C++的有什么區(qū)別

  • C語言的結(jié)構(gòu)體是不能有函數(shù)成員的,而C++的類可以有。
  • C語言的結(jié)構(gòu)體中數(shù)據(jù)成員是沒有private、public和protected訪問限定的。而C++的類的成員有這些訪問限定。
  • C語言的結(jié)構(gòu)體是沒有繼承關(guān)系的,而C++的類卻有豐富的繼承關(guān)系。

「注意」:雖然C的結(jié)構(gòu)體和C++的類有很大的相似度,但是類是實現(xiàn)面向?qū)ο蟮幕A(chǔ)。而結(jié)構(gòu)體只可以簡單地理解為類的前身。

26 簡述指針常量與常量指針的區(qū)別

  • 指針常量是指定義了一個指針,這個指針的值只能在定義時初始化,其他地方不能改變。常量指針是指定義了一個指針,這個指針指向一個只讀的對象,不能通過常量指針來改變這個對象的值。
  • 指針常量強調(diào)的是指針的不可改變性,而常量指針強調(diào)的是指針對其所指對象的不可改變性。

「注意」:無論是指針常量還是常量指針,其最大的用途就是作為函數(shù)的形式參數(shù),保證實參在被調(diào)用函數(shù)中的不可改變特性。

27 如何避免“野指針”

  • 指針變量聲明時沒有被初始化。解決辦法:指針聲明時初始化,可以是具體的地址值,也可讓它指向NULL。
  • 指針p被free或者delete之后,沒有置為NULL。解決辦法:指針指向的內(nèi)存空間被釋放后指針應(yīng)該指向NULL。
  • 指針操作超越了變量的作用范圍。解決辦法:在變量的作用域結(jié)束前釋放掉變量的地址空間并且讓指針指向NULL。

28 句柄和指針的區(qū)別和聯(lián)系是什么?

句柄和指針其實是兩個截然不同的概念。Windows系統(tǒng)用句柄標記系統(tǒng)資源,隱藏系統(tǒng)的信息。你只要知道有這個東西,然后去調(diào)用就行了,它是個32it的uint。指針則標記某個物理內(nèi)存地址,兩者是不同的概念。

29 new/delete與malloc/free的區(qū)別是什么

  • new能自動計算需要分配的內(nèi)存空間,而malloc需要手工計算字節(jié)數(shù)。
int *p = new int[2];
int *q = (int *)malloc(2*sizeof(int));
  • new與delete直接帶具體類型的指針,malloc和free返回void類型的指針。
  • new類型是安全的,而malloc不是。例如int *p = new float[2];就會報錯;而int p = malloc(2sizeof(int))編譯時編譯器就無法指出錯誤來。
  • new一般分為兩步:new操作和構(gòu)造。new操作對應(yīng)與malloc,但new操作可以重載,可以自定義內(nèi)存分配策略,不做內(nèi)存分配,甚至分配到非內(nèi)存設(shè)備上,而malloc不行。
  • new調(diào)用構(gòu)造函數(shù),malloc不能;delete調(diào)用析構(gòu)函數(shù),而free不能。
  • malloc/free需要庫文件stdlib.h的支持,new/delete則不需要!

「注意」:delete和free被調(diào)用后,內(nèi)存不會立即回收,指針也不會指向空,delete或free僅僅是告訴操作系統(tǒng),這一塊內(nèi)存被釋放了,可以用作其他用途。但是由于沒有重新對這塊內(nèi)存進行寫操作,所以內(nèi)存中的變量數(shù)值并沒有發(fā)生變化,出現(xiàn)野指針的情況。因此,釋放完內(nèi)存后,應(yīng)該講該指針指向NULL。

30 說一說extern“C”

extern "C"的主要作用就是為了能夠正確實現(xiàn)C++代碼調(diào)用其他C語言代碼。加上extern "C"后,會指示編譯器這部分代碼按C語言(而不是C++)的方式進行編譯。由于C++支持函數(shù)重載,因此編譯器編譯函數(shù)的過程中會將函數(shù)的參數(shù)類型也加到編譯后的代碼中,而不僅僅是函數(shù)名;而C語言并不支持函數(shù)重載,因此編譯C語言代碼的函數(shù)時不會帶上函數(shù)的參數(shù)類型,一般只包括函數(shù)名。

這個功能十分有用處,因為在C++出現(xiàn)以前,很多代碼都是C語言寫的,而且很底層的庫也是C語言寫的,為了更好的支持原來的C代碼和已經(jīng)寫好的C語言庫,需要在C++中盡可能的支持C,而extern "C"就是其中的一個策略。

  • C++代碼調(diào)用C語言代碼
  • 在C++的頭文件中使用
  • 在多個人協(xié)同開發(fā)時,可能有的人比較擅長C語言,而有的人擅長C++,這樣的情況下也會有用到

31 請你來說一下C++中struct和class的區(qū)別

在C++中,class和struct做類型定義是只有兩點區(qū)別:

  • 默認繼承權(quán)限不同,class繼承默認是private繼承,而struct默認是public繼承
  • class還可用于定義模板參數(shù),像typename,但是關(guān)鍵字struct不能同于定義模板參數(shù) C++保留struct關(guān)鍵字,原因
  • 保證與C語言的向下兼容性,C++必須提供一個struct
  • C++中的struct定義必須百分百地保證與C語言中的struct的向下兼容性,把C++中的最基本的對象單元規(guī)定為class而不是struct,就是為了避免各種兼容性要求的限制
  • 對struct定義的擴展使C語言的代碼能夠更容易的被移植到C++中

32 C++類內(nèi)可以定義引用數(shù)據(jù)成員嗎?

可以,必須通過成員函數(shù)初始化列表初始化。

33 C++中類成員的訪問權(quán)限

C++通過 public、protected、private 三個關(guān)鍵字來控制成員變量和成員函數(shù)的訪問權(quán)限,它們分別表示公有的、受保護的、私有的,被稱為成員訪問限定符。在類的內(nèi)部(定義類的代碼內(nèi)部),無論成員被聲明為 public、protected 還是 private,都是可以互相訪問的,沒有訪問權(quán)限的限制。在類的外部(定義類的代碼之外),只能通過對象訪問成員,并且通過對象只能訪問 public 屬性的成員,不能訪問 private、protected 屬性的成員

34 什么是右值引用,跟左值又有什么區(qū)別?

左值和右值的概念:

  • 左值:能取地址,或者具名對象,表達式結(jié)束后依然存在的持久對象;
  • 右值:不能取地址,匿名對象,表達式結(jié)束后就不再存在的臨時對象;區(qū)別:
  • 左值能賦值,右值不能;
  • 左值可變,右值不能(僅對基礎(chǔ)類型適用,用戶自定義類型右值引用可以通過成員函數(shù)改變);

35 面向?qū)ο蟮娜筇卣?/span>

  • 封裝性:將客觀事物抽象成類,每個類對自身的數(shù)據(jù)和方法實行 protection (private , protected , public )。
  • 繼承性:廣義的繼承有三種實現(xiàn)形式:實現(xiàn)繼承(使用基類的屬性和方法而無需額外編碼的能力)、可 視繼承(子窗體使用父窗體的外觀和實現(xiàn)代碼)、接口繼承(僅使用屬性和方法,實現(xiàn)滯后到子類實現(xiàn))。
  • 多態(tài)性:是將父類對象設(shè)置成為和一個或更多它的子對象相等的技術(shù)。用子類對象給父類對象賦值 之后,父類對象就可以根據(jù)當前賦值給它的子對象的特性以不同的方式運作。

36 說一說c++中四種cast轉(zhuǎn)換

C++中四種類型轉(zhuǎn)換是:static_cast, dynamic_cast, const_cast, reinterpret_cast

1、const_cast

  • 用于將const變量轉(zhuǎn)為非const

2、static_cast

  • 用于各種隱式轉(zhuǎn)換,比如非const轉(zhuǎn)const,void*轉(zhuǎn)指針等, static_cast能用于多態(tài)向上轉(zhuǎn)化,如果向下轉(zhuǎn)能成功但是不安全,結(jié)果未知;

3、dynamic_cast

用于動態(tài)類型轉(zhuǎn)換。只能用于含有虛函數(shù)的類,用于類層次間的向上和向下轉(zhuǎn)化。只能轉(zhuǎn)指針或引用。向下轉(zhuǎn)化時,如果是非法的***對于指針返回NULL,對于引用拋異常***。要深入了解內(nèi)部轉(zhuǎn)換的原理。

  • 向上轉(zhuǎn)換:指的是子類向基類的轉(zhuǎn)換
  • 向下轉(zhuǎn)換:指的是基類向子類的轉(zhuǎn)換

它通過判斷在執(zhí)行到該語句的時候變量的運行時類型和要轉(zhuǎn)換的類型是否相同來判斷是否能夠進行向下轉(zhuǎn)換。

4、reinterpret_cast

  • 幾乎什么都可以轉(zhuǎn),比如將int轉(zhuǎn)指針,可能會出問題,盡量少用;

5、為什么不使用C的強制轉(zhuǎn)換?

  • C的強制轉(zhuǎn)換表面上看起來功能強大什么都能轉(zhuǎn),但是轉(zhuǎn)化不夠明確,不能進行錯誤檢查,容易出錯。

37 C++的空類有哪些成員函數(shù)

  • 缺省構(gòu)造函數(shù)。
  • 缺省拷貝構(gòu)造函數(shù)。
  • 省析構(gòu)函數(shù)。
  • 賦值運算符。
  • 取址運算符。
  • 取址運算符 const 。

「注意」:有些書上只是簡單的介紹了前四個函數(shù)。沒有提及后面這兩個函數(shù)。但后面這兩個函數(shù)也是 空類的默認函數(shù)。另外需要注意的是,只有當實際使用這些函數(shù)的時候,編譯器才會去定義它們。

38 對c++中的smart pointer四個智能指針:shared_ptr,unique_ptr,weak_ptr,auto_ptr的理解

C++里面的四個智能指針: auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三個是c++11支持,并且第一個已經(jīng)被11棄用。

智能指針的作用是管理一個指針,因為存在以下這種情況:申請的空間在函數(shù)結(jié)束時忘記釋放,造成內(nèi)存泄漏。使用智能指針可以很大程度上的避免這個問題,因為智能指針就是一個類,當超出了類的作用域是,類會自動調(diào)用析構(gòu)函數(shù),析構(gòu)函數(shù)會自動釋放資源。所以智能指針的作用原理就是在函數(shù)結(jié)束時自動釋放內(nèi)存空間,不需要手動釋放內(nèi)存空間。

  • auto_ptr(c++98的方案,cpp11已經(jīng)拋棄)

采用所有權(quán)模式。

auto_ptr< string> p1 (new string ("I reigned lonely as a cloud.”));
auto_ptrp2;
p2 = p1; //auto_ptr不會報錯. 

此時不會報錯,p2剝奪了p1的所有權(quán),但是當程序運行時訪問p1將會報錯。所以auto_ptr的缺點是:存在潛在的內(nèi)存崩潰問題!

  • unique_ptr(替換auto_ptr)

unique_ptr實現(xiàn)獨占式擁有或嚴格擁有概念,保證同一時間內(nèi)只有一個智能指針可以指向該對象。它對于避免資源泄露(例如“以new創(chuàng)建對象后因為發(fā)生異常而忘記調(diào)用delete”)特別有用。

采用所有權(quán)模式。

unique_ptrp3 (new string ("auto"));   //#4 unique_ptrp4;                       //#5 p4 = p3;//此時會報錯??!

編譯器認為p4=p3非法,避免了p3不再指向有效數(shù)據(jù)的問題。因此,unique_ptr比auto_ptr更安全。

另外unique_ptr還有更聰明的地方:當程序試圖將一個 unique_ptr 賦值給另一個時,如果源 unique_ptr 是個臨時右值,編譯器允許這么做;如果源 unique_ptr 將存在一段時間,編譯器將禁止這么做,比如:

unique_ptrpu1(new string ("hello world"));
unique_ptrpu2;
pu2 = pu1;                                      // #1 not allowed unique_ptrpu3;
pu3 = unique_ptr(new string ("You"));   // #2 allowed 

其中#1留下懸掛的unique_ptr(pu1),這可能導(dǎo)致危害。而#2不會留下懸掛的unique_ptr,因為它調(diào)用 unique_ptr 的構(gòu)造函數(shù),該構(gòu)造函數(shù)創(chuàng)建的臨時對象在其所有權(quán)讓給 pu3 后就會被銷毀。這種隨情況而已的行為表明,unique_ptr 優(yōu)于允許兩種賦值的auto_ptr 。

「注意」:如果確實想執(zhí)行類似與#1的操作,要安全的重用這種指針,可給它賦新值。C++有一個標準庫函數(shù)std::move(),讓你能夠?qū)⒁粋€unique_ptr賦給另一個。例如:

unique_ptrps1, ps2;
ps1 = demo("hello");
ps2 = move(ps1);
ps1 = demo("alexia");
cout << *ps2 << *ps1 << endl;
  • shared_ptr

shared_ptr實現(xiàn)共享式擁有概念。多個智能指針可以指向相同對象,該對象和其相關(guān)資源會在“最后一個引用被銷毀”時候釋放。從名字share就可以看出了資源可以被多個指針共享,它使用計數(shù)機制來表明資源被幾個指針共享??梢酝ㄟ^成員函數(shù)use_count()來查看資源的所有者個數(shù)。除了可以通過new來構(gòu)造,還可以通過傳入auto_ptr, unique_ptr,weak_ptr來構(gòu)造。當我們調(diào)用release()時,當前指針會釋放資源所有權(quán),計數(shù)減一。當計數(shù)等于0時,資源會被釋放。

shared_ptr 是為了解決 auto_ptr 在對象所有權(quán)上的局限性(auto_ptr 是獨占的), 在使用引用計數(shù)的機制上提供了可以共享所有權(quán)的智能指針。

成員函數(shù):

use_count 返回引用計數(shù)的個數(shù)

unique 返回是否是獨占所有權(quán)( use_count 為 1)

swap 交換兩個 shared_ptr 對象(即交換所擁有的對象)

reset 放棄內(nèi)部對象的所有權(quán)或擁有對象的變更, 會引起原有對象的引用計數(shù)的減少

get 返回內(nèi)部對象(指針), 由于已經(jīng)重載了()方法, 因此和直接使用對象是一樣的.如 shared_ptrsp(new int(1)); sp 與 sp.get()是等價的

  • weak_ptr

weak_ptr 是一種不控制對象生命周期的智能指針, 它指向一個 shared_ptr 管理的對象. 進行該對象的內(nèi)存管理的是那個強引用的 shared_ptr. weak_ptr只是提供了對管理對象的一個訪問手段。weak_ptr 設(shè)計的目的是為配合 shared_ptr 而引入的一種智能指針來協(xié)助 shared_ptr 工作, 它只可以從一個 shared_ptr 或另一個 weak_ptr 對象構(gòu)造, 它的構(gòu)造和析構(gòu)不會引起引用記數(shù)的增加或減少。weak_ptr是用來解決shared_ptr相互引用時的死鎖問題,如果說兩個shared_ptr相互引用,那么這兩個指針的引用計數(shù)永遠不可能下降為0,資源永遠不會釋放。它是對對象的一種弱引用,不會增加對象的引用計數(shù),和shared_ptr之間可以相互轉(zhuǎn)化,shared_ptr可以直接賦值給它,它可以通過調(diào)用lock函數(shù)來獲得shared_ptr。

class B;
class A
{
public:
shared_ptr pb_;
~A()
{
     cout<<"A delete
";
}
};
class B
{
public:
shared_ptr pa_;
~B()
{
    cout<<"B delete
";
}
};
void fun()
{
    shared_ptr pb(new B());
    shared_ptr pa(new A());
    pb->pa_ = pa;
    pa->pb_ = pb;
    cout<main()
{
    fun(); return 0;
}

可以看到fun函數(shù)中pa ,pb之間互相引用,兩個資源的引用計數(shù)為2,當要跳出函數(shù)時,智能指針pa,pb析構(gòu)時兩個資源引用計數(shù)會減一,但是兩者引用計數(shù)還是為1,導(dǎo)致跳出函數(shù)時資源沒有被釋放(A B的析構(gòu)函數(shù)沒有被調(diào)用),如果把其中一個改為weak_ptr就可以了,我們把類A里面的shared_ptr pb_; 改為weak_ptr pb_; 運行結(jié)果如下,這樣的話,資源B的引用開始就只有1,當pb析構(gòu)時,B的計數(shù)變?yōu)?,B得到釋放,B釋放的同時也會使A的計數(shù)減一,同時pa析構(gòu)時使A的計數(shù)減一,那么A的計數(shù)為0,A得到釋放。

「注意」:不能通過weak_ptr直接訪問對象的方法,比如B對象中有一個方法print(),我們不能這樣訪問,pa->pb_->print(); 英文pb_是一個weak_ptr,應(yīng)該先把它轉(zhuǎn)化為shared_ptr,如:shared_ptr p = pa->pb_.lock(); p->print();

39 說說強制類型轉(zhuǎn)換運算符

「static_cast」

  • 用于非多態(tài)類型的轉(zhuǎn)換
  • 不執(zhí)行運行時類型檢查(轉(zhuǎn)換安全性不如 dynamic_cast)
  • 通常用于轉(zhuǎn)換數(shù)值數(shù)據(jù)類型(如 float -> int)
  • 可以在整個類層次結(jié)構(gòu)中移動指針,子類轉(zhuǎn)化為父類安全(向上轉(zhuǎn)換),父類轉(zhuǎn)化為子類不安全(因為子類可能有不在父類的字段或方法)

「dynamic_cast」

  • 用于多態(tài)類型的轉(zhuǎn)換
  • 執(zhí)行行運行時類型檢查
  • 只適用于指針或引用
  • 對不明確的指針的轉(zhuǎn)換將失?。ǚ祷?nullptr),但不引發(fā)異常
  • 可以在整個類層次結(jié)構(gòu)中移動指針,包括向上轉(zhuǎn)換、向下轉(zhuǎn)換

「const_cast」

  • 用于刪除 const、volatile 和 __unaligned 特性(如將 const int 類型轉(zhuǎn)換為 int 類型 ) reinterpret_cast
  • 用于位的簡單重新解釋
  • 濫用 reinterpret_cast 運算符可能很容易帶來風險。除非所需轉(zhuǎn)換本身是低級別的,否則應(yīng)- 使用其他強制轉(zhuǎn)換運算符之一。
  • 允許將任何指針轉(zhuǎn)換為任何其他指針類型(如 char* 到 int* 或 One_class* 到  Unrelated_class* 之類的轉(zhuǎn)換,但其本身并不安全)
  • 也允許將任何整數(shù)類型轉(zhuǎn)換為任何指針類型以及反向轉(zhuǎn)換。
  • reinterpret_cast 運算符不能丟掉 const、volatile 或 __unaligned 特性。
  • reinterpret_cast 的一個實際用途是在哈希函數(shù)中,即,通過讓兩個不同的值幾乎不以相同的索引結(jié)尾的方式將值映射到索引。

「bad_cast」

  • 由于強制轉(zhuǎn)換為引用類型失敗,dynamic_cast 運算符引發(fā) bad_cast 異常。

bad_cast 使用

try {
    Circle& ref_circle = dynamic_cast(ref_shape);
}
catch (bad_cast b) {
    cout << "Caught: " << b.what(); }

40 談?wù)勀銓截悩?gòu)造函數(shù)和賦值運算符的認識

拷貝構(gòu)造函數(shù)和賦值運算符重載有以下兩個不同之處:

  • 拷貝構(gòu)造函數(shù)生成新的類對象,而賦值運算符不能。
  • 由于拷貝構(gòu)造函數(shù)是直接構(gòu)造一個新的類對象,所以在初始化這個對象之前不用檢驗源對象 是否和新建對象相同。而賦值運算符則需要這個操作,另外賦值運算中如果原來的對象中有內(nèi)存分配要先把內(nèi)存釋放掉。

「注意」:當有類中有指針類型的成員變量時,一定要重寫拷貝構(gòu)造函數(shù)和賦值運算符,不要使用默認 的。

41 在C++中,使用malloc申請的內(nèi)存能否通過delete釋放?使用new申請的內(nèi)存能否用free?

不能,malloc /free主要為了兼容C,new和delete 完全可以取代malloc /free的。malloc /free的操作對象都是必須明確大小的。而且不能用在動態(tài)類上。new 和delete會自動進行類型檢查和大小,malloc/free不能執(zhí)行構(gòu)造函數(shù)與析構(gòu)函數(shù),所以動態(tài)對象它是不行的。當然從理論上說使用malloc申請的內(nèi)存是可以通過delete釋放的。不過一般不這樣寫的。而且也不能保證每個C++的運行時都能正常。

42 用C++設(shè)計一個不能被繼承的類

templateclass A 
{ 
   friend T; 
    private: A() {} 
    ~A() {} 
}; 
class B : virtual public A { 
   public: B() {} 
   ~B() {} 
}; 
class C : virtual public B 
{ 
   public: C() {} 
    ~C() {} 
}; 
void main( void ) 
{ 
    B b; 
    //C c; return; 
}

「注意」:構(gòu)造函數(shù)是繼承實現(xiàn)的關(guān)鍵,每次子類對象構(gòu)造時,首先調(diào)用的是父類的構(gòu)造函數(shù),然后才 是自己的。

43 C++自己實現(xiàn)一個String類

#include #include using namespace std;
 
class String{
public:
    // 默認構(gòu)造函數(shù)
    String(const char *str = nullptr);
    // 拷貝構(gòu)造函數(shù)
    String(const String &str);
    // 析構(gòu)函數(shù)
    ~String();
    // 字符串賦值函數(shù)
    String& operator=(const String &str);
 
private:
    char *m_data;
    int m_size;
};
 
// 構(gòu)造函數(shù)
String::String(const char *str)
{ if(str == nullptr)  // 加分點:對m_data加NULL 判斷
    {
        m_data = new char[1];   // 得分點:對空字符串自動申請存放結(jié)束標志'\0'的
        m_data[0] = '\0';
        m_size = 0;
    } else {
        m_size = strlen(str);
        m_data = new char[m_size + 1];
        strcpy(m_data, str);
    }
}
 
// 拷貝構(gòu)造函數(shù)
String::String(const String &str)   // 得分點:輸入?yún)?shù)為const型
{
    m_size = str.m_size;
    m_data = new char[m_size + 1];  //加分點:對m_data加NULL 判斷
    strcpy(m_data, str.m_data);
}
 
// 析構(gòu)函數(shù)
String::~String()
{
    delete[] m_data;
}
 
// 字符串賦值函數(shù)
String& String::operator=(const String &str)  // 得分點:輸入?yún)?shù)為const
{ if(this == &str)    //得分點:檢查自賦值 return *this;
 
    delete[] m_data;    //得分點:釋放原有的內(nèi)存資源
    m_size = strlen(str.m_data);
    m_data = new char[m_size + 1];  //加分點:對m_data加NULL 判斷
    strcpy(m_data, str.m_data); return *this;       //得分點:返回本對象的引用
}

44 訪問基類的私有虛函數(shù)

寫出以下程序的輸出結(jié)果:

#include   class A
{ 
   virtual void g() 
   { 
      cout << "A::g" << endl; } private: virtual void f() 
   { 
      cout << "A::f" << endl; } }; class B : public A { void g() 
   { 
      cout << "B::g" << endl; } virtual void h() 
   { 
      cout << "B::h" << endl; } }; typedef void( *Fun )( void ); void main() 
{ 
   B b; 
   Fun pFun; for(int i = 0 ; i < 3; i++) { pFun = ( Fun )*( ( int* ) * ( int* )( &b ) + i ); pFun(); } }

輸出結(jié)果:

B::g 
A::f 
B::h

「注意」:考察了面試者對虛函數(shù)的理解程度。一個對虛函數(shù)不了解的人很難正確的做出本題。在學(xué)習面向?qū)ο蟮亩鄳B(tài)性時一定要深刻理解虛函數(shù)表的工作原理。

45 對虛函數(shù)和多態(tài)的理解

多態(tài)的實現(xiàn)主要分為靜態(tài)多態(tài)和動態(tài)多態(tài),靜態(tài)多態(tài)主要是重載,在編譯的時候就已經(jīng)確定;動態(tài)多態(tài)是用虛函數(shù)機制實現(xiàn)的,在運行期間動態(tài)綁定。舉個例子:一個父類類型的指針指向一個子類對象時候,使用父類的指針去調(diào)用子類中重寫了的父類中的虛函數(shù)的時候,會調(diào)用子類重寫過后的函數(shù),在父類中聲明為加了virtual關(guān)鍵字的函數(shù),在子類中重寫時候不需要加virtual也是虛函數(shù)。

虛函數(shù)的實現(xiàn):在有虛函數(shù)的類中,類的最開始部分是一個虛函數(shù)表的指針,這個指針指向一個虛函數(shù)表,表中放了虛函數(shù)的地址,實際的虛函數(shù)在代碼段(.text)中。當子類繼承了父類的時候也會繼承其虛函數(shù)表,當子類重寫父類中虛函數(shù)時候,會將其繼承到的虛函數(shù)表中的地址替換為重新寫的函數(shù)地址。使用了虛函數(shù),會增加訪問內(nèi)存開銷,降低效率。

46 簡述類成員函數(shù)的重寫、重載和隱藏的區(qū)別

(1)重寫和重載主要有以下幾點不同。

  • 范圍的區(qū)別:被重寫的和重寫的函數(shù)在兩個類中,而重載和被重載的函數(shù)在同一個類中。
  • 參數(shù)的區(qū)別:被重寫函數(shù)和重寫函數(shù)的參數(shù)列表一定相同,而被重載函數(shù)和重載函數(shù)的參數(shù)列表一 定不同。
  • virtual 的區(qū)別:重寫的基類中被重寫的函數(shù)必須要有virtual 修飾,而重載函數(shù)和被重載函數(shù)可以被 virtual 修飾,也可以沒有。

(2)隱藏和重寫、重載有以下幾點不同。

  • 與重載的范圍不同:和重寫一樣,隱藏函數(shù)和被隱藏函數(shù)不在同一個類中。
  • 參數(shù)的區(qū)別:隱藏函數(shù)和被隱藏的函數(shù)的參數(shù)列表可以相同,也可不同,但是函數(shù)名肯定要相同。當參數(shù)不相同時,無論基類中的參數(shù)是否被virtual 修飾,基類的函數(shù)都是被隱藏,而不是被重寫。

「注意」:雖然重載和覆蓋都是實現(xiàn)多態(tài)的基礎(chǔ),但是兩者實現(xiàn)的技術(shù)完全不相同,達到的目的也是完 全不同的,覆蓋是動態(tài)態(tài)綁定的多態(tài),而重載是靜態(tài)綁定的多態(tài)。

47 鏈表和數(shù)組有什么區(qū)別

  • 存儲形式:數(shù)組是一塊連續(xù)的空間,聲明時就要確定長度。鏈表是一塊可不連續(xù)的動態(tài)空間, 長度可變,每個結(jié)點要保存相鄰結(jié)點指針。
  • 數(shù)據(jù)查找:數(shù)組的線性查找速度快,查找操作直接使用偏移地址。鏈表需要按順序檢索結(jié)點, 效率低。
  • 數(shù)據(jù)插入或刪除:鏈表可以快速插入和刪除結(jié)點,而數(shù)組則可能需要大量數(shù)據(jù)移動。
  • 越界問題:鏈表不存在越界問題,數(shù)組有越界問題。

「注意」:在選擇數(shù)組或鏈表數(shù)據(jù)結(jié)構(gòu)時,一定要根據(jù)實際需要進行選擇。數(shù)組便于查詢,鏈表便于插 入刪除。數(shù)組節(jié)省空間但是長度固定,鏈表雖然變長但是占了更多的存儲空間。

48 用兩個棧實現(xiàn)一個隊列的功能

typedef struct node 
{ 
 int data; 
 node *next; 
}node,*LinkStack; 
 
//創(chuàng)建空棧: 
LinkStack CreateNULLStack( LinkStack &S) 
{ 
 S = (LinkStack)malloc( sizeof( node ) ); // 申請新結(jié)點 if( NULL == S) 
 { printf("Fail to malloc a new node.\n"); return NULL; 
 } 
 S->data = 0; //初始化新結(jié)點 
 S->next = NULL; return S; 
} 
 
//棧的插入函數(shù): 
LinkStack Push( LinkStack &S, int data) 
{ if( NULL == S) //檢驗棧 
 { printf("There no node in stack!"); return NULL; 
 } 
 
 LinkStack p = NULL; 
 p = (LinkStack)malloc( sizeof( node ) ); // 申請新結(jié)點 if( NULL == p) 
 { printf("Fail to malloc a new node.\n"); return S; 
 } if( NULL == S->next) 
 { 
  p->next = NULL; 
 } else { 
  p->next = S->next; 
 } 
 p->data = data; //初始化新結(jié)點 
 S->next = p; //插入新結(jié)點 return S; 
} 
 
//出棧函數(shù): 
node Pop( LinkStack &S) 
{ 
 node temp; 
 temp.data = 0; 
 temp.next = NULL; if( NULL == S) //檢驗棧 
 { printf("There no node in stack!"); return temp; 
 } 
 temp = *S; if( S->next == NULL ) 
 { printf("The stack is NULL,can't pop!\n"); return temp; 
 } 
 LinkStack p = S ->next; //節(jié)點出棧 
 
 S->next = S->next->next; 
 temp = *p; 
 free( p ); 
 p = NULL; return temp; 
} 
 
//雙棧實現(xiàn)隊列的入隊函數(shù): 
LinkStack StackToQueuPush( LinkStack &S, int data) 
{ 
 node n; 
 LinkStack S1 = NULL; 
 CreateNULLStack( S1 ); //創(chuàng)建空棧 while( NULL != S->next ) //S 出棧入S1 
 { 
  n = Pop( S ); 
  Push( S1, n.data ); 
 } 
 Push( S1, data ); //新結(jié)點入棧 while( NULL != S1->next ) //S1 出棧入S 
 { 
  n = Pop( S1 ); 
  Push( S, n.data ); 
 } return S; 
}

「注意」:用兩個棧能夠?qū)崿F(xiàn)一個隊列的功能,那用兩個隊列能否實現(xiàn)一個隊列的功能呢?結(jié)果是否定 的,因為棧是先進后出,將兩個棧連在一起,就是先進先出。而隊列是現(xiàn)先進先出,無論多少個連在一 起都是先進先出,而無法實現(xiàn)先進后出。

49 模板函數(shù)和模板類的特例化

「引入原因」

編寫單一的模板,它能適應(yīng)多種類型的需求,使每種類型都具有相同的功能,但對于某種特定類型,如果要實現(xiàn)其特有的功能,單一模板就無法做到,這時就需要模板特例化

「定義」對單一模板提供的一個特殊實例,它將一個或多個模板參數(shù)綁定到特定的類型或值上

(1)模板函數(shù)特例化

必須為原函數(shù)模板的每個模板參數(shù)都提供實參,且使用關(guān)鍵字template后跟一個空尖括號對<>,表明將原模板的所有模板參數(shù)提供實參,舉例如下:

template//模板函數(shù)
int compare(const T &v1,const T &v2)
{ if(v1 > v2) return -1; if(v2 > v1) return 1; return 0;
}
//模板特例化,滿足針對字符串特定的比較,要提供所有實參,這里只有一個T
template<> 
int compare(const char* const &v1,const char* const &v2)
{ return strcmp(p1,p2);
}

「本質(zhì)」特例化的本質(zhì)是實例化一個模板,而非重載它。特例化不影響參數(shù)匹配。參數(shù)匹配都以最佳匹配為原則。例如,此處如果是compare(3,5),則調(diào)用普通的模板,若為compare(“hi”,”haha”)則調(diào)用特例化版本(因為這個cosnt char*相對于T,更匹配實參類型),注意二者函數(shù)體的語句不一樣了,實現(xiàn)不同功能。

「注意」模板及其特例化版本應(yīng)該聲明在同一個頭文件中,且所有同名模板的聲明應(yīng)該放在前面,后面放特例化版本。

(2)類模板特例化

原理類似函數(shù)模板,不過在類中,我們可以對模板進行特例化,也可以對類進行部分特例化。對類進行特例化時,仍然用template<>表示是一個特例化版本,例如:

template<>
class hash{
    size_t operator()(sales_data& s);
    //里面所有T都換成特例化類型版本sales_data
    //按照最佳匹配原則,若T != sales_data,就用普通類模板,否則,就使用含有特定功能的特例化版本。
};

「類模板的部分特例化」

不必為所有模板參數(shù)提供實參,可以指定一部分而非所有模板參數(shù),一個類模板的部分特例化本身仍是一個模板,使用它時還必須為其特例化版本中未指定的模板參數(shù)提供實參(特例化時類名一定要和原來的模板相同,只是參數(shù)類型不同,按最佳匹配原則,哪個最匹配,就用相應(yīng)的模板)

「特例化類中的部分成員」

可以特例化類中的部分成員函數(shù)而不是整個類,舉個例子:

templateclass Foo
{
    void Bar();
    void Barst(T a)();
};

template<>
void Foo::Bar()
{
    //進行int類型的特例化處理
    cout << "我是int型特例化" << endl; } Foofs; Foofi;//使用特例化
fs.Bar();//使用的是普通模板,即Foo::Bar()
fi.Bar();//特例化版本,執(zhí)行Foo::Bar()
//Foo::Bar()和Foo::Bar()功能不同

50 為什么析構(gòu)函數(shù)一般寫成虛函數(shù)

由于類的多態(tài)性,基類指針可以指向派生類的對象,如果刪除該基類的指針,就會調(diào)用該指針指向的派生類析構(gòu)函數(shù),而派生類的析構(gòu)函數(shù)又自動調(diào)用基類的析構(gòu)函數(shù),這樣整個派生類的對象完全被釋放。如果析構(gòu)函數(shù)不被聲明成虛函數(shù),則編譯器實施靜態(tài)綁定,在刪除基類指針時,只會調(diào)用基類的析構(gòu)函數(shù)而不調(diào)用派生類析構(gòu)函數(shù),這樣就會造成派生類對象析構(gòu)不完全,造成內(nèi)存泄漏。所以將析構(gòu)函數(shù)聲明為虛函數(shù)是十分必要的。在實現(xiàn)多態(tài)時,當用基類操作派生類,在析構(gòu)時防止只析構(gòu)基類而不析構(gòu)派生類的狀況發(fā)生,要將基類的析構(gòu)函數(shù)聲明為虛函數(shù)。舉個例子:

#include using namespace std;

class Parent{
public: Parent(){
        cout << "Parent construct function" << endl; }; ~Parent(){
        cout << "Parent destructor function" <Son(){
        cout << "Son construct function" << endl; }; ~Son(){
        cout << "Son destructor function" <main()
{
    Parent* p = new Son();
    delete p;
    p = NULL; return 0;
}
//運行結(jié)果:
//Parent construct function //Son construct function //Parent destructor function 

將基類的析構(gòu)函數(shù)聲明為虛函數(shù):

#include using namespace std;

class Parent{
public: Parent(){
        cout << "Parent construct function" << endl; }; virtual ~Parent(){
        cout << "Parent destructor function" <Son(){
        cout << "Son construct function" << endl; }; ~Son(){
        cout << "Son destructor function" <main()
{
    Parent* p = new Son();
    delete p;
    p = NULL; return 0;
}
//運行結(jié)果:
//Parent construct function //Son construct function //Son destructor function //Parent destructor function 

51 vector的底層原理

vector底層是一個動態(tài)數(shù)組,包含三個迭代器,start和finish之間是已經(jīng)被使用的空間范圍,end_of_storage是整塊連續(xù)空間包括備用空間的尾部。

當空間不夠裝下數(shù)據(jù)(vec.push_back(val))時,會自動申請另一片更大的空間(1.5倍或者2倍),然后把原來的數(shù)據(jù)拷貝到新的內(nèi)存空間,接著釋放原來的那片空間[vector內(nèi)存增長機制]。

當釋放或者刪除(vec.clear())里面的數(shù)據(jù)時,其存儲空間不釋放,僅僅是清空了里面的數(shù)據(jù)。因此,對vector的任何操作一旦引起了空間的重新配置,指向原vector的所有迭代器會都失效了。

52 vector中的reserve和resize的區(qū)別

  • reserve是直接擴充到已經(jīng)確定的大小,可以減少多次開辟、釋放空間的問題(優(yōu)化push_back),就可以提高效率,其次還可以減少多次要拷貝數(shù)據(jù)的問題。reserve只是保證vector中的空間大?。╟apacity)最少達到參數(shù)所指定的大小n。reserve()只有一個參數(shù)。
  • resize()可以改變有效空間的大小,也有改變默認值的功能。capacity的大小也會隨著改變。resize()可以有多個參數(shù)。

53 vector中的size和capacity的區(qū)別

  • size表示當前vector中有多少個元素(finish - start);
  • capacity函數(shù)則表示它已經(jīng)分配的內(nèi)存中可以容納多少元素(end_of_storage - start);

54 vector中erase方法與algorithn中的remove方法區(qū)別

  • vector中erase方法真正刪除了元素,迭代器不能訪問了
  • remove只是簡單地將元素移到了容器的最后面,迭代器還是可以訪問到。因為algorithm通過迭代器進行操作,不知道容器的內(nèi)部結(jié)構(gòu),所以無法進行真正的刪除。

55 vector迭代器失效的情況

  • 當插入一個元素到vector中,由于引起了內(nèi)存重新分配,所以指向原內(nèi)存的迭代器全部失效。
  • 當刪除容器中一個元素后,該迭代器所指向的元素已經(jīng)被刪除,那么也造成迭代器失效。erase方法會返回下一個有效的迭代器,所以當我們要刪除某個元素時,需要it=vec.erase(it);。

56 正確釋放vector的內(nèi)存(clear(), swap(), shrink_to_fit())

  • vec.clear():清空內(nèi)容,但是不釋放內(nèi)存。
  • vector().swap(vec):清空內(nèi)容,且釋放內(nèi)存,想得到一個全新的vector。
  • vec.shrink_to_fit():請求容器降低其capacity和size匹配。
  • vec.clear();vec.shrink_to_fit();:清空內(nèi)容,且釋放內(nèi)存。

57 list的底層原理

  • ist的底層是一個雙向鏈表,使用鏈表存儲數(shù)據(jù),并不會將它們存儲到一整塊連續(xù)的內(nèi)存空間中。恰恰相反,各元素占用的存儲空間(又稱為節(jié)點)是獨立的、分散的,它們之間的線性關(guān)系通過指針來維持,每次插入或刪除一個元素,就配置或釋放一個元素空間。
  • list不支持隨機存取,如果需要大量的插入和刪除,而不關(guān)心隨即存取

58 什么情況下用vector,什么情況下用list,什么情況下用deque

  • vector可以隨機存儲元素(即可以通過公式直接計算出元素地址,而不需要挨個查找),但在非尾部插入刪除數(shù)據(jù)時,效率很低,適合對象簡單,對象數(shù)量變化不大,隨機訪問頻繁。除非必要,我們盡可能選擇使用vector而非deque,因為deque的迭代器比vector迭代器復(fù)雜很多。
  • list不支持隨機存儲,適用于對象大,對象數(shù)量變化頻繁,插入和刪除頻繁,比如寫多讀少的場景。
  • 需要從首尾兩端進行插入或刪除操作的時候需要選擇deque。

59 priority_queue的底層原理

priority_queue:優(yōu)先隊列,其底層是用堆來實現(xiàn)的。在優(yōu)先隊列中,隊首元素一定是當前隊列中優(yōu)先級最高的那一個。

60 map 、set、multiset、multimap的底層原理

map 、set、multiset、multimap的底層實現(xiàn)都是紅黑樹,epoll模型的底層數(shù)據(jù)結(jié)構(gòu)也是紅黑樹,linux系統(tǒng)中CFS進程調(diào)度算法,也用到紅黑樹。

紅黑樹的特性:

  • 每個結(jié)點或是紅色或是黑色;
  • 根結(jié)點是黑色;
  • 每個葉結(jié)點是黑的;
  • 如果一個結(jié)點是紅的,則它的兩個兒子均是黑色;
  • 每個結(jié)點到其子孫結(jié)點的所有路徑上包含相同數(shù)目的黑色結(jié)點。

61 為何map和set的插入刪除效率比其他序列容器高

因為不需要內(nèi)存拷貝和內(nèi)存移動

62 為何map和set每次Insert之后,以前保存的iterator不會失效?

因為插入操作只是結(jié)點指針換來換去,結(jié)點內(nèi)存沒有改變。而iterator就像指向結(jié)點的指針,內(nèi)存沒變,指向內(nèi)存的指針也不會變。

63 當數(shù)據(jù)元素增多時(從10000到20000),map的set的查找速度會怎樣變化?

RB-TREE用二分查找法,時間復(fù)雜度為logn,所以從10000增到20000時,查找次數(shù)從log10000=14次到log20000=15次,多了1次而已。

64 map 、set、multiset、multimap的特點

  • set和multiset會根據(jù)特定的排序準則自動將元素排序,set中元素不允許重復(fù),multiset可以重復(fù)。
  • map和multimap將key和value組成的pair作為元素,根據(jù)key的排序準則自動將元素排序(因為紅黑樹也是二叉搜索樹,所以map默認是按key排序的),map中元素的key不允許重復(fù),multimap可以重復(fù)。
  • map和set的增刪改查速度為都是logn,是比較高效的。

65 為何map和set的插入刪除效率比其他序列容器高,而且每次insert之后,以前保存的iterator不會失效?

  • 存儲的是結(jié)點,不需要內(nèi)存拷貝和內(nèi)存移動。
  • 插入操作只是結(jié)點指針換來換去,結(jié)點內(nèi)存沒有改變。而iterator就像指向結(jié)點的指針,內(nèi)存沒變,指向內(nèi)存的指針也不會變。

66 為何map和set不能像vector一樣有個reserve函數(shù)來預(yù)分配數(shù)據(jù)?

在map和set內(nèi)部存儲的已經(jīng)不是元素本身了,而是包含元素的結(jié)點。也就是說map內(nèi)部使用的Alloc并不是map聲明的時候從參數(shù)中傳入的Alloc。

67 set的底層實現(xiàn)實現(xiàn)為什么不用哈希表而使用紅黑樹?

set中元素是經(jīng)過排序的,紅黑樹也是有序的,哈希是無序的 如果只是單純的查找元素的話,那么肯定要選哈希表了,因為哈希表在的最好查找時間復(fù)雜度為O(1),并且如果用到set中那么查找時間復(fù)雜度的一直是O(1),因為set中是不允許有元素重復(fù)的。而紅黑樹的查找時間復(fù)雜度為O(lgn)

68 hash_map與map的區(qū)別?什么時候用hash_map,什么時候用map?

  • 構(gòu)造函數(shù):hash_map需要hash function和等于函數(shù),而map需要比較函數(shù)(大于或小于)。
  • 存儲結(jié)構(gòu):hash_map以hashtable為底層,而map以RB-TREE為底層。
  • 總的說來,hash_map查找速度比map快,而且查找速度基本和數(shù)據(jù)量大小無關(guān),屬于常數(shù)級別。而map的查找速度是logn級別。但不一定常數(shù)就比log小,而且hash_map還有hash function耗時。
  • 如果考慮效率,特別當元素達到一定數(shù)量級時,用hash_map。
  • 考慮內(nèi)存,或者元素數(shù)量較少時,用map。

69 迭代器失效的問題

插入操作:

  • 對于vector和string,如果容器內(nèi)存被重新分配,iterators,pointers,references失效;如果沒有重新分配,那么插入點之前的iterator有效,插入點之后的iterator失效;
  • 對于deque,如果插入點位于除front和back的其它位置,iterators,pointers,references失效;當我們插入元素到front和back時,deque的迭代器失效,但reference和pointers有效;
  • 對于list和forward_list,所有的iterator,pointer和refercnce有效。刪除操作:
  • 對于vector和string,刪除點之前的iterators,pointers,references有效;off-the-end迭代器總是失效的;
  • 對于deque,如果刪除點位于除front和back的其它位置,iterators,pointers,references失效;當我們插入元素到front和back時,off-the-end失效,其他的iterators,pointers,references有效;
  • 對于list和forward_list,所有的iterator,pointer和refercnce有效。
  • 對于關(guān)聯(lián)容器map來說,如果某一個元素已經(jīng)被刪除,那么其對應(yīng)的迭代器就失效了,不應(yīng)該再被使用,否則會導(dǎo)致程序無定義的行為。

70 STL線程不安全的情況

  • 在對同一個容器進行多線程的讀寫、寫操作時;
  • 在每次調(diào)用容器的成員函數(shù)期間都要鎖定該容器;
  • 在每個容器返回的迭代器(例如通過調(diào)用begin或end)的生存期之內(nèi)都要鎖定該容器;
  • 在每個在容器上調(diào)用的算法執(zhí)行期間鎖定該容器。


免責聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉