C++編程筆記-特性

一、虛函數

  虛函數一般是定義在基類中聲明爲 virtual 並在一個或多個派生類中被重新定義的成員函數,用法格式爲:virtual 函數返回類型 函數名(){}。虛函數的作用就是用於運行多態性,即在類繼承中,基類和派生類會有相同的成員函數,指向基類的指針在操作它的多態類對象時(這個指針可以指向基類的對象,也可以指向派生類的對象),會根據不同的類對象,調用其相應的函數,這個函數就是虛函數。一般的成員函數(包括虛函數)可以只有聲明,前提是在應用中不能調用該函數,否則會因找不到定義產生連接錯誤!

class A{
public:
    void function(){}
}
class B : public A{
public:
    void function(){}
}
int main(){
    A a;
    B b;
    A *p1 = &a;
    A *p2 = &b;
    p1->function();
    p2->function();
}

  在上面的代碼中我們定義了基類A和派生類B,若function函數不是虛函數時,則輸出時p1和p2調用的都是基類A的函數function,因爲它們都是A類型的指針,若是在基類A的function函數前面添加virtual使之成爲虛函數,則輸出時p1和p2調用的函數就分別是A和B的function函數了。這就是虛函數的作用,即在基類指針指向多態對象時,根據對象所屬的類來調用相應的函數。
虛函數使用事項:
   1. 在基類用virtual聲明成員函數爲虛函數。 這樣就可以在派生類中重新定義此函數,爲它賦予新的功能,並能方便地被調用。在類外定義虛函數時,不必再加virtual。
   2. 在派生類中重新定義此函數,要求函數名、函數類型、函數參數個數和類型全部與基類的虛函數相同,並根據派生類的需要重新定義函數體。C++規定,當一個成員函數被聲明爲虛函數後,其派生類中的同名函數都自動成爲虛函數。因此在派生類重新聲明該虛函數時,可以加virtual,也可以不加,但習慣上一般在每一層聲明該函數時都加virtual,使程序更加清晰。如果在派生類中沒有對基類的虛函數重新定義,則派生類簡單地繼承其直接基類的虛函數。
   3. 定義一個指向基類對象的指針變量,並使它指向同一類族中需要調用該函數的對象。
通過該指針變量調用此虛函數,此時調用的就是指針變量指向的對象的同名函數。通過虛函數與指向基類對象的指針變量的配合使用,就能方便地調用同一類族中不同類的同名函數,只要先用基類指針指向即可。如果指針不斷地指向同一類族中不同類的對象,就能不斷地調用這些對象中的同名函數。
此節部分參考內容及詳細實例可以參考《什麼是C++虛函數、虛函數的作用和使用方法》
C++的多態性更多內容參考[C++之多態性與虛函數](http://www.cnblogs.com
只有類的成員函數才能說明爲虛函數,因爲虛函數僅適合用與有繼承關係的類對象,所以普通函數不能說明爲虛函數。
靜態成員函數不能是虛函數,因爲靜態成員函數的特點是不受限制於某個對象。
內聯(inline)函數不能是虛函數,因爲內聯函數不能在運行中動態確定位置。即使虛函數在類的內部定義,但是在編譯的時候系統仍然將它看做是非內聯的。
構造函數不能是虛函數,因爲構造的時候,對象還是一片位定型的空間,只有構造完成後,對象纔是具體類的實例。析構函數可以是虛函數,而且通常聲名爲虛函數。

純虛函數:即基類中沒有給出有意義的實現的虛函數,類似於Java中的接口函數。純虛函數一般都直接=0,表明他的直接功能由子類進行定義,他是虛函數的其中一種。派生類中必須要實現純虛函數。
純虛類:只要有一個純虛函數,就稱爲純虛類也叫抽象類,純虛類不可以定義一個對象。
Java有關虛函數等於C++的區別:在Java中一般沒有虛函數的概念,Java繼承中最常見的概念就是抽象類和接口,關於兩者區別聯繫參考:JAVA – 虛函數、抽象函數、抽象類、接口

二、虛析構函數

  C++中與構造函數對應的就是析構函數了,我們都知道析構函數的作用就是用於釋放內存清除不需要的數據空間。一般是在構造函數前加一個~就是析構函數了,這裏要講的虛析構函數就是在析構函數前再加上virtual。虛析構函數的作用就是:爲了當用一個基類的指針刪除一個派生類的對象時,派生類的析構函數會被調用。一般來講也只有當一個類用作基類時才把析構函數寫成虛析構函數。其實這就是虛函數在析構函數上的一種應用,詳細的講解可參考C++中虛析構函數的作用
  一般來說,帶多態性質的基類應該聲明一個virtual析構函數,一個類如果不是用於基類或是多態的化,不應該聲明virtual析構函數。常見的string,STL都不被設計爲基類使用,即在這些類中,不存在virtual析構函數,最好不要用來當作基類進行派生。

三、#program once

  在有些代碼中可以看到這一句:#program once,這個意思就是當前文件不會被包含多次,當前文件應用上指的是物理上的一個文件,它的作用於最常見的#ifndef #define #endif作用是相似的。具體區別參考:#ifndef 與 #program once 的區別

四、typedef #define

  typedef和#define都可以用來定義別名,但兩者的順序剛好相反,如把INT定義成C++的數據類型int,即INT爲int的別名,則兩種寫法分別爲:
  typedef int INT;
  #define INT int;
  這裏的typedef定義的只是一個別名,而#define定義的是一個宏,雖然也有別名的一個功能。
作用域:
   typedef:
   如果放在所有函數之外,它的作用域就是從它定義開始直到文件尾;
   如果放在某個函數內,定義域就是從定義開始直到該函數結尾;
   #define:
   不管是在某個函數內,還是在所有函數之外,作用域都是從定義開始直到整個文件結尾。不管是typedef還是define,其作用域都不會擴展到別的文件,即使是同一個程序的不同文件,也不能互相使用。
   typedef和#define的區別

五、類定義相關函數

  類定義中經常要定義構造函數和成員函數,但是有時需要注意,不然很容易出錯。
爲了便於管理,最後在類頭文件中進行聲明,然後在類.cpp文件中定義。這樣最不容易出錯。在.cpp中定義每一個函數前都要加上類名::這個作用域符號。在頭文件聲明類成員的時候不能給變量賦值。
  對於構造函數和析構函數,要是有聲明,就一定要定義實現,即使只是在後面加上{},也是可以的,不然沒有定義會出現錯誤。
  可以經常看到一些類成員函數在聲明的時候就直接定義了,如下面的getX():

class C
{
public:
    //C(){};
    int getX(){return x;}
private:
    int x;
};
/*不要在下面定義
int C::getX()
{
    return x;
}*/

函數直接在聲明時就定義(在類聲明中定義)的將被自動當作inline函數。一般函數都是在相應的.cpp文件中定義,但是注意最好不要在類外面定義,即在類聲明完後在類外面同一個頭文件中定義,有時是沒有問題的,但是當這個文件被包含時,經常會發生重複定義的錯誤。因此定義個函數時最好按照標準的方式來進行。總結如下:
(1)成員函數定義放在類裏面的是被自動當做inline
(2)如果類函數定義在類的外面但在.h文件裏就必須要顯式聲明inline,否則當這個頭文件被多個文件包含時就會被認爲重複定義
(3)如果類成員函數在.cpp文件裏面定義就不用加了,你想加也可以,但不是必須的。
參考:http://bbs.chinaunix.net/thread-1571848-1-1.html

六、inline函數

  內聯函數,C++中在函數返回類型前面添加inline用於標識這個函數爲內聯函數。在編譯過程中內聯函數會直接被源代碼替換,提高執行效率 如果類中的某個函數會被調用很多次或者放在循環中,那麼建議將這個函數聲明爲內聯,可以提高程序的運行效率。它類似於宏定義的函數塊,但相對於宏又有更好的優點。使用inline函數注意下面事項:

  1. 定義在類聲明之中的成員函數將自動地成爲內聯函數。
  2. 關鍵字inline 必須與函數定義體放在一起才能使函數成爲內聯,僅將inline 放在函數聲明前面不起任何作用。
  3. 內聯函數代碼要儘量簡短。
  4. 內聯函數在最終生成的代碼中是沒有定義的,C++ 編譯器直接將函數體插入函數調用的地方,內聯函數沒有普通函數調用時的額外開銷 ( 壓棧,跳轉,返回 ) 。
  5. inline並不能決定的使一個函數進行內聯編譯,也就是說這個還取決於編譯器,因此沒有inline的函數也可能被編譯器優化稱爲內聯編譯。
  6. 等等。。。
    參考1:http://www.tuicool.com/articles/jaeEjqA
    參考2:http://www.cnblogs.com/berry/articles/1582702.html

七、explicit

  explicit是C++的一個關鍵字,用於阻止不應該允許的經過轉換構造函數進行的隱式轉換的發生。聲明爲explicit的構造函數不能在隱式轉換中使用。

class Test1
{
public:
    Test1(int n){num=n;}//普通構造函數
private:
    int num;
};
class Test2
{
public:
    explicit Test2(int n){num=n;}//explicit(顯式)構造函數
private:
    int num;
};
int main()
{
    Test1 t1=12;//隱式調用其構造函數,成功
    Test2 t2=12;//編譯錯誤,不能隱式調用其構造函數
    Test2 t2(12);//顯式調用成功
    return 0;
}

  Test1的構造函數帶一個int型的參數,代碼23行會隱式轉換成調用Test1的這個構造函數。而Test2的構造函數被聲明爲explicit(顯式),這表示不能通過隱式轉換來調用這個構造函數,因此代碼 Test2 t2=12會出現編譯錯誤。普通構造函數能夠被隱式調用,而explicit構造函數只能被顯式調用。
  參考文章explicit

八、heap(堆)和stack(棧)

  1.heap是堆,stack是棧。
  2.stack的空間由操作系統自動分配和釋放,heap的空間是手動申請和釋放的,heap常用new關鍵字或malloc()函數來分配,需要手動釋放。
  3.stack空間有限,heap的空間是很大的自由區。
  詳細參考:堆(heap)和棧(stack)有什麼區別??

九、C和C++結構體的區別

  1. C中的結構體只能定義數據變量,不允許有函數,只是涉及數據結構。
  2. C++中的結構體不僅能定義數據變量,還能定義成員函數,可以被繼承可以有虛函數,和類相似可以有構造函數等。
  3. C中的數據只是公有的,而C++中的可以是公有、私有、保護等。
  4. struct和typedef struct兩種定義方式在C和C++中表示的意義不同。
在C中:
  struct Test{ int a; }; 表示結構體Test,創建一個結構體方式:struct Test Ex;
  typedef struct Test{int a;}Te; 表示結構體Test,創建一個結構體方式:struct Test Ex或Te Ex;
在C++中:
  struct Test{int a;}; 表示結構體Test,創建一個結構體方式:Test Ex;
  struct Test{int a;}Te;   表示結構體Test,Te是一個結構體變量;
  typedef struct Test{ int a;}Te;  表示結構體Test,Te是一個結構體類型;
總結:typedef struct 在C和C++中區別不大,都是定義類型,主要區別在於struct。

十、C++中int數據

  int在C++中是一個常見的數據類型,一般爲4個字節,32位有符號的數據,大小範圍爲-2^31~2^31-1;最大值和最小值有宏定義INT_MAX, INT_MIN。
  long 一般在32位系統中是和int一樣的,64位系統是8字節,long long 爲8字節
  
  unsigned int 0~4294967295
  int -2147483648~2147483647
  unsigned long 0~4294967295
  long -2147483648~2147483647
  long long的最大值:9223372036854775807
  long long的最小值:-9223372036854775808
  unsigned long long的最大值:1844674407370955161

  __int64的最大值:9223372036854775807
  __int64的最小值:-9223372036854775808
  unsigned __int64的最大值:18446744073709551615

C++中做運算,若是超過範圍則會溢出(會有結果但不正確),因此要注意。其次需要注意的是對於最小的負值(如-2147483648)它的絕對值等於其本身,而不是2147483648,因爲2147483648已經溢出了,參考代碼之謎

十一、C++構造函數

  構造函數用來初始化一個對象,當沒有自己定義構造函數時,編譯器將自動爲你生成一個默認的無參構造函數,如果自己定義了,編譯器將不會提供默認構造函數。構造函數、析構函數、賦值操作符,這些都不能被繼承。另外對於基類的靜態成員,無論有多少個派生類,都只有這一個靜態成員實例。 因此在派生類中一般都要有自己的構造函數嗎,派生類中構造函數的調用順爲先調用基類的構造函數再調用派生類的構造函數,析構函數相反。
  通過派生類創建對象時必須要調用基類的構造函數,這是語法規定。也就是說,定義派生類構造函數時最好指明基類構造函數;如果不指明,就調用基類的默認構造函數(不帶參數的構造函數);如果沒有默認構造函數,那麼編譯失敗。
  當我們在初始化一個子類對象的時候,而這個子類對象的父類有一個顯示的帶有參數的構造函數,需要使用成員初值化。
  如果沒有聲明,則編譯器會自動聲明下面幾個函數:default構造函數,copy構造函數,copy assignment構造函數,析構函數,當你定義了構造函數,他就不在再提供,或當你定義了copy函數,他就不再提供默認的copy構造函數。

十二、C++成員初始化列表

class A{
public:
    A(int b){num = b;}
    A(int b):num(b){};//成員初始化列表
    int num;
};

  成員初始化列表和構造函數的函數體都可以爲我們的類數據成員指定一些初值,但是兩者在給成員指定初值的方式上是不同的。成員初始化列表使用初始化的方式來爲數據成員指定初值,而構造函數的函數體是通過賦值的方式來給數據成員指定初值。也就是說,成員初始化列表是在數據成員定義的同時賦初值,但是構造函的函數體是採用先定義後賦值的方式來做。這樣的區別就造成了,在有些場景下,是必須要使用成員初始化列表的。  
  1、初始化一個引用成員變量
  2、初始化一個const變量
  3、當我們在初始化一個子類對象的時候,而這個子類對象的父類有一個顯示的帶有參數的構造函數
  4、當調用一個類類型成員的構造函數,而它擁有一組參數的時候
  
  在這四種情況下是必須要使用成員初始化列表來爲這些類型的成員賦初值的。因爲這些成員都必須用初始化的方式賦初值。比如引用和const成員變量,這些都是不接受定義之後的賦值的。而對於3的話,是因爲子類對象包括父類對象,父類對象既然明確指定了帶參的構造函數,那麼就必須在構造子類對象的父類部分時顯示調用這個構造函數,是不能依賴於合成的默認構造函數的,而這樣的話,就必須在成員初始化列表中調用。4也一樣,類類型的成員所在類如果有顯示定義的構造函數那麼也是需要在定義這個成員的同時需要顯示調用的。
  對於第四點,需要注意的是當類中有其他類類型的成員變量時,只有當成員是該類類型時才需要初值化列表,若是該類類型的指針或是以該類作爲vector,queue等容器的類型時,也是不需要的,因爲這裏該成員變量類行是容器類型,不是純粹的該類類型。
  成員初值化構造函數要不要參數由成員決定,當需要從外面引入參數時就加。
  大多數情況下成員初值化是更加節省資源的,因此推薦使用。
  C++成員初始化列表詳解

十三、C++支持的4種變量存儲類型

在C++中支持4種變量存儲類型:
1、auto 自動變量  2、register 寄存器變量  3、extern 外部變量  4、static 靜態變量

auto:auto稱爲自動變量(局部變量)。局部變量是指在函數內部說明的變量。所有的非全局變量都被認爲是局部變量,所以auto實際上從來不用。局部變量在函數調用時自動產生,但不會自動初始化,隨函數調用的結束,這個變量也就自動消失了。下次調用此函數時再自動產生,還要再賦值,退出時又自動消失。

static:static稱爲靜態變量。根據變量的類型可以分爲靜態局部變量和靜態全局變量。
  1. 靜態局部變量:它與局部變量的區別在於:在函數退出時,這個變量始終存在,但不能被其它函數使用;當再次進入該函數時,將保存上次的結果。其它與局部變量一樣。
  2. 靜態全局變量:只在定義它的源文件中可見而在其它源文件中不可見的變量。它與全局變量的區別是:全局變量可以被其它源文件使用,而靜態全局變量只能被所在的源文件使用。
  
extern:extern稱爲外部申明。全局變量是在函數的外部定義的,它的作用域爲從變量的定義處開始,到本程序文件的末尾。在此作用域內,全局變量可以爲本文件中各個函數所引用。爲了使變量或者函數除了在定義它的源文件中可以使用外,還可以被其它文件使用,需要用extern來聲明全局變量,以擴展全局變量的作用域。

register:register稱爲寄存器變量。它只能用於整型和字符型變量。定義符register說明的變量被存儲在CPU的寄存器中,定義一個整型寄存器變量可寫成: register int a;
對於以上四種數據的存儲位置:register變量存在CPU的寄存器中;auto類型變量存在內存的棧;static型的局部變量和全局變量以及extern型變量(即全局變量),存在於內存的靜態區。

十四、動態鏈接和靜態鏈接的區別

庫(lib):所謂的庫就是一些功能代碼經過編譯連接後的可執行形式。
  在編寫代碼的時候,經常會遇到需要使用別人已經寫好的代碼的情況,即利用別人的程序庫在自己的代碼中實現部分功能,這就涉及到靜態鏈接和動態鏈接了。

靜態鏈接:就是在生成可執行文件的時候,把所需要的函數的二進制代碼包含到可執行文件中去。在程序發佈的時候就不需要的依賴庫,也就是不再需要帶着庫一塊發佈,程序可以獨立執行。程序體積會相對大一些。如果靜態庫有更新的話,所有可執行文件都得重新鏈接才能用上新的靜態庫。

動態鏈接:就是在編譯的時候不直接拷貝可執行代碼,而是通過記錄一系列的符號和參數,在程序運行或加載時將這些信息傳遞給操作系統,操作系統負責將需要的動態庫加載到內存中,當程序運行到指定代碼時,去共享執行已經加載到內存中的動態可執行代碼,最終達到運行時鏈接的目的。多個程序可以共享同一段代碼,而不需要在磁盤上存儲多個拷貝。由於是運行時加載,可能會影響程序的前期執行性能。

windows的dll文件和linux的os等都是動態庫;lib,A庫等爲靜態庫,其中也包含有動態庫。
靜態鏈接與動態鏈接的區別

十五、C++ 值傳遞、指針傳遞、引用傳遞

值傳遞:形參是實參的拷貝,改變形參的值並不會影響外部實參的值,即形參和實參是相互獨立的,只不過形參用了實參進行賦值。

指針傳遞:也是一種值傳遞,不過傳遞的是指向數據的地址,函數生成一個拷貝指針,和實參指針指向相同的數據,當更改指針指向的值時,會實時傳遞到實際的參數中去,可以用來修改並返回值,同時指針本身的值也可以改變,指向其他的值,這時不會影響到實參。

引用傳遞:傳遞的只是實參的一個別名,在函數中的任何修改,都會反映到實參中去,可以視爲實際處理的就是實參。

指針與引用的:都是地址的概念;指針指向一塊內存,它的內容是所指內存的地址;而引用則是某塊內存的別名。區別:指針是一個實體,而引用僅是個別名;引用只能在定義時被初始化一次,之後不可變;指針可變;引用“從一而終”,指針可以“見異思遷”;引用沒有const,指針有const,const的指針不可變;引用不能爲空,指針可以爲空;“sizeof 引用”得到的是所指向的變量(對象)的大小,而“sizeof 指針”得到的是指針本身的大小;指針和引用的自增(++)運算意義不一樣;引用是類型安全的,而指針不是 (引用比指針多了類型檢查)。

十六、C++多態實現機制

C++中的多態性具體體現在運行和編譯兩個方面:
運行時多態:是動態多態,其具體引用的對象在運行時才能確定。般通過虛函數(virtual function)+繼承實現。
編譯時多態:是靜態多態,在編譯時就可以確定對象使用的形式,例如函數重載、運算符重載,模板等。
一般討論C++多態實現都是指運行時多態。它是通過虛函數和繼承來實現的,實際上是通過虛表來實現的;在C++語言中,每個有虛函數的類或者虛繼承的子類,編譯器都會爲它生成一個虛擬函數表(簡稱:虛表),虛表是從屬於類的,表中的每一個元素都指向一個虛函數的地址。此外,編譯器會爲包含虛函數的類加上一個成員變量,是一個指向該虛函數表的指針(常被稱爲vptr),每一個由此類別派生出來的類,都有這麼一個vptr。虛表指針是從屬於對象的,也就是說,如果一個類含有虛表,則該類的所有對象都會含有一個虛表指針,並且該虛表指針指向同一個虛表。基類有虛函數的時候,類中有虛函數指針,指向虛表,該虛表中是基類的函數實現版本。如果子類有自己的實現,會在自己的虛函數表中替換子類的實現版本。當通過指針訪問時候,通過vptr定位到的函數就是具體的類的實現版本,就是多態。
c++多態實現的機制

十七、new和malloc的異同

兩者都是動態申請內存空間,new是C++裏的操作符,用於動態創建數組或單個對象,返回相應的指針,利用delete釋放內存;而malloc是C裏的函數,申請一個內存空間返回void *指針指向內存空間,利用free來釋放。主要區別:
1、new 返回指定類型的指針,並且可以自動計算所需要大小。而 malloc 則必須要由我們計算字節數,並且在返回後強行轉換爲實際類型的指針。
2、malloc 只管分配內存,並不能對所得的內存進行初始化,所以得到的一片新內存中,其值將是隨機的。new創建的對象可以用初始化變量的方式初始化。

十八、static

靜態全局變量:

  • 該變量在全局數據區分配內存;
  • 未經初始化的靜態全局變量會被程序自動初始化爲0(自動變量的值是隨機的,除非它被顯式初始化);
  • 靜態全局變量在聲明它的整個文件都是可見的,而在文件之外是不可見的; 
  • 靜態全局變量不能被其它文件所用,其它文件中可以定義相同名字的變量,不會發生衝突;

靜態局部變量:

  • 該變量在全局數據區分配內存;
  • 靜態局部變量在程序執行到該對象的聲明處時被首次初始化,即以後的函數調用不再進行初始化;
  • 靜態局部變量一般在聲明處初始化,如果沒有顯式初始化,會被程序自動初始化爲0;
  • 它始終駐留在全局數據區,直到程序運行結束。但其作用域爲局部作用域,當定義它的函數或語句塊結束時,其作用域隨之結束;

靜態函數:
  在函數的返回類型前加上static關鍵字,函數即被定義爲靜態函數。靜態函數與普通函數不同,它只能在聲明它的文件當中可見,不能被其它文件使用。

類中的static:
靜態數據成員:
  在類內數據成員的聲明前加上關鍵字static,該數據成員就是類內的靜態數據成員

  • 對於非靜態數據成員,每個類對象都有自己的拷貝。而靜態數據成員被當作是類的成員。無論這個類的對象被定義了多少個,靜態數據成員在程序中也只有一份拷貝,由該類型的所有對象共享訪問。也就是說,靜態數據成員是該類的所有對象所共有的。對該類的多個對象來說,靜態數據成員只分配一次內存,供所有對象共用。所以,靜態數據成員的值對每個對象都是一樣的,它的值可以更新;
  • 靜態數據成員存儲在全局數據區。靜態數據成員定義時要分配空間,所以不能在類聲明中定義,一般在cpp文件中定義。
  • 靜態數據成員和普通數據成員一樣遵從public,protected,private訪問規則;
  • 因爲靜態數據成員在全局數據區分配內存,屬於本類的所有對象共享,所以,它不屬於特定的類對象,在沒有產生類對象時其作用域就可見,即在沒有產生類的實例時,我們就可以操作它;
  • 靜態數據成員初始化與一般數據成員初始化不同。靜態數據成員初始化的格式爲:
    <數據類型><類名>::<靜態數據成員名>=<值>
    類的靜態數據成員有兩種訪問形式:
    <類對象名>.<靜態數據成員名> 或 <類類型名>::<靜態數據成員名>
  • 如果靜態數據成員的訪問權限允許的話(即public的成員),可在程序中,按上述格式來引用靜態數據成員 ;
  • 靜態數據成員主要用在各個對象都有相同的某項屬性的時候。比如對於一個存款類,每個實例的利息都是相同的。所以,應該把利息設爲存款類的靜態數據成員。這有兩個好處,第一,不管定義多少個存款類對象,利息數據成員都共享分配在全局數據區的內存,所以節省存儲空間。第二,一旦利息需要改變時,只要改變一次,則所有存款類對象的利息全改變過來了;
  • 靜態數據成員不能用靜態函數初始化,因爲靜態成員在程序執行前就被已經存在;

同全局變量相比,使用靜態數據成員有兩個優勢:

  • 靜態數據成員沒有進入程序的全局名字空間,因此不存在與程序中其它全局名字衝突的可能性;
  • 可以實現信息隱藏。靜態數據成員可以是private成員,而全局變量不能;

靜態成員函數:
  與靜態數據成員一樣,它爲類的全部服務而不是爲某一個類的具體對象服務。靜態成員函數與靜態數據成員一樣,都是類的內部實現,屬於類定義的一部分。普通的成員函數一般都隱含了一個this指針,this指針指向類的對象本身,因爲普通成員函數總是具體的屬於某個類的具體對象的。通常情況下,this是缺省的。如函數fn()實際上是this->fn()。但是與普通函數相比,靜態成員函數由於不是與任何的對象相聯繫,因此它不具有this指針。從這個意義上講,它無法訪問屬於類對象的非靜態數據成員,也無法訪問非靜態成員函數,它只能調用其餘的靜態成員函數。

  • 出現在類體外的函數定義不能指定關鍵字static;
  • 靜態成員之間可以相互訪問,包括靜態成員函數訪問靜態數據成員和訪問靜態成員函數;
  • 非靜態成員函數可以任意地訪問靜態成員函數和靜態數據成員;
  • 靜態成員函數不能訪問非靜態成員函數和非靜態數據成員;
  • 由於沒有this指針的額外開銷,因此靜態成員函數與類的全局函數相比速度上會有少許的增長;
  • 調用靜態成員函數,可以用成員訪問操作符(.)和(->)爲一個類的對象或指向類對象的指針調用靜態成員函數,也可以直接使用如下格式:
    <類名>::<靜態成員函數名>(<參數表>)調用類的靜態成員函數。

十九、程序的內存分佈

一個程序運行過程中佔用的內存中主要有5個部分:

  • 棧區(stack): 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。
  • 堆區(heap):一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由操作系統回收,注意它與數據結構中的堆是兩回事。
  • 全局區(靜態區)(static)—,全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域, 未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。
  • 文字常量區 —常量字符串就是放在這裏的,程序結束後由系統釋放 。
  • 程序代碼區—存放函數體的二進制代碼。
int main(){char* ch = "what's the f";...}
//ch爲指針是保存在棧中的,但是字符串what's...則是在文件常量區的,因此不能更改ch指向的值,但是可以更改ch的值,使之指向其它地方。

二十、C和C++區別

C:C語言是一種結構化語言,重點在於算法和數據結構,首要考慮的是如何通過一個過程對輸入進行運算處理得到輸出,是面向過程的。
C++:C++兼容C,首要考慮的是如何構造一個對象模型,通過對象來進行相應的處理得到輸出,是面向對象的。
C++的特性:與C語言相比,C++有幾大特性:繼承、封裝、多態。
封裝:封裝是把過程和數據包圍起來,對數據的訪問只能通過定義的方式進行,封裝可以隱藏實現細節,使得代碼模塊化,並提供了代碼重用性,可以隔離數據,保護內部數據不被隨意修改。
繼承:子類通過繼承父類可以得到父類的部分特性,可以擴展已存在的代碼模塊,提高代碼的重用性。
多態:就是爲了接口重用,即同一個接口根據對象類型的不同可以調用不同的方法。
STL是C++提供的一個標準庫,封裝了許多容器和算法,具有很強大的功能,方便實用。

二十一、深複製和淺複製

  在有指針的情況下,淺拷貝只是增加了一個指針指向已經存在的內存,而深拷貝就是增加一個指針並且申請一個新的內存,使這個增加的指針指向這個新的內存,採用深拷貝的情況下,釋放內存的時候就不會出現在淺拷貝時重複釋放同一內存的錯誤。

二十二、extern

extern 變量;聲明外部全局變量,說明變量在外邊文件定義
extern C;告訴編譯器下面的代碼按C語言進行編譯,而不是C++,C++與C的編譯區別主要在於C++函數支持重載,函數編譯後的代碼中不僅有函數的名稱,還有函數的參數類型,而C一般只有函數的名稱。
http://www.jianshu.com/p/5d2eeeb93590

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章