C++知識點系列之一(轉+整理)ios
編程時類聲明後面千萬不要忘了加分號,否則會出現不少錯誤!!程序員
c系列之一
1、#include 「filename.h」和#include<filename.h> 的區別
#include 「filename.h」是指編譯器將從當前工做目錄上開始查找此文件
#include<filename.h> 是指編譯器將從標準庫目錄中開始查找此文件
2、頭文件的做用
增強安全檢測
經過頭文件可能方便地調用庫功能,而沒必要關心其實現方式
3、* , &修飾符的位置
對於*和&修飾符,爲了不誤解,最好將修飾符緊靠變量名
4、if語句
不要將布爾變量與任何值進行比較,那會很容易出錯的。
整形變量必需要有類型相同的值進行比較
浮點變量最好少比點,就算要比也要有值進行限制
指針變量要和NULL進行比較,不要和布爾型和整形比較
5、const和#define的比較
const有數據類型,#define沒有數據類型
個別編譯器中const能夠進行調試,#define不能夠進行調試
在類中定義常量有兩種方式
一、 在類在聲明常量,但不賦值,在構造函數初始化表中進行賦值;
二、 用枚舉代替const常量。
6、C++函數中值的傳遞方式
有三種方式:值傳遞(Pass by value)、指針傳遞(Pass by pointer)、引用傳遞(Pass by reference)
void fun(char c) //pass by value
void fun(char *str) //pass by pointer
void fun(char &str) //pass by reference
若是輸入參數是以值傳遞的話,最好使用引用傳遞代替,由於引用傳遞省去了臨時對象的構造和析構
函數的類型不能省略,就算沒有也要加個void
7、函數體中的指針或引用常量不能被返回
Char *func(void)
{
char str[]=」Hello Word」;
//這個是不能被返回的,由於str是個指定變量,不是通常的值,函數結束後會被註銷掉
return str;
}
函數體內的指針變量並不會隨着函數的消亡而自動釋放
8、一個內存拷貝函數的實現體
void *memcpy(void *pvTo,const void *pvFrom,size_t size)
{
assert((pvTo!=NULL)&&(pvFrom!=NULL));
byte *pbTo=(byte*)pvTo; //防止地址被改變
byte *pbFrom=(byte*)pvFrom;
while (size-- >0)
pbTo++ = pbForm++;
return pvTo;
}
9、內存的分配方式
分配方式有三種,請記住,說不定那天去面試的時候就會有人問你這問題
一、 靜態存儲區,是在程序編譯時就已經分配好的,在整個運行期間都存在,如全局變量、常量。
二、 棧上分配,函數內的局部變量就是從這分配的,但分配的內存容易有限。
三、 堆上分配,也稱動態分配,如咱們用new,malloc分配內存,用delete,free來釋放的內存。
10、內存分配的注意事項
用new或malloc分配內存時,必需要對此指針賦初值。
用delete 或free釋放內存後,必需要將指針指向NULL
不能修改指向常量的指針數據
11、內容複製與比較
//數組……
char a[]=」Hello Word!」;
char b[10];
strcpy(b,a);
if (strcmp(a,b)==0)
{}
//指針……
char a[]=」Hello Word!」;
char *p;
p=new char[strlen(a)+1];
strcpy(p,a);
if (strcmp(p,a)==0)
{}
12、sizeof的問題
記住一點,C++沒法知道指針所指對象的大小,指針的大小永遠爲4字節
char a[]=」Hello World!」
char *p=a;
count<<
count<<
並且,在函數中,數組參數退化爲指針,因此下面的內容永遠輸出爲4
void fun(char a[1000])
{
count<<
}
十3、關於指針
一、 指針建立時必須被初始化
二、 指針在free 或delete後必須置爲NULL
三、 指針的長度都爲4字節
4、釋放內存時,若是是數組指針,必需要釋放掉全部的內存,如
char *p=new char[100];
strcpy(p,」Hello World」);
delete []p; //注意前面的[]號
p=NULL;
5、數組指針的內容不能超過數組指針的最大容易。
如:
char *p=new char[5];
strcpy(p,」Hello World」); //報錯 目標容易不夠大
delete []p; //注意前面的[]號
p=NULL;
十4、關於malloc/free 和new /delete
malloc/free 是C/C+的內存分配符,new /delete是C++的內存分配符。
注意:malloc/free是庫函數,new/delete是運算符
malloc/free不能執行構造函數與析構函數,而new/delete能夠
new/delete不能在C上運行,因此malloc/free不能被淘汰
二者都必需要成對使用
C++中可使用_set_new_hander函數來定義內存分配異常的處理
十5、C++的特性
C++新增長有重載(overload),內聯(inline),Const,Virtual四種機制
重載和內聯:便可用於全局函數,也可用於類的成員函數;
Const和Virtual:只可用於類的成員函數;
重載:在同一類中,函數名相同的函數。由不一樣的參數決定調用那個函數。函數可要不可要Virtual關鍵字。和全局函數同名的函數不叫重載。若是在類中調用同名的全局函數,必須用全局引用符號::引用。
覆蓋是指派生類函數覆蓋基類函數
函數名相同;
參數相同;
基類函數必須有Virtual關鍵字;
不一樣的範圍(派生類和基類)。
隱藏是指派生類屏蔽了基類的同名函數相同
一、 函數名相同,但參數不一樣,此時不論基類有無Virtual關鍵字,基類函數將被隱藏。
二、 函數名相同,參數也相同,但基類無Virtual關鍵字(有就是覆蓋),基類函數將被隱藏。
內聯:inline關鍵字必須與定義體放在一塊兒,而不是單單放在聲明中。
Const:const是constant的縮寫,「恆定不變」的意思。被const修飾的東西都受到強制保護,能夠預防意外的變更,能提升程序的健壯性。
一、 參數作輸入用的指針型參數,加上const可防止被意外改動。
二、按值引用的用戶類型作輸入參數時,最好將按值傳遞的改成引用傳遞,並加上const關鍵字,目的是爲了提升效率。數據類型爲內部類型的就不必作這件事情;如:
將void Func(A a) 改成void Func(const A &a)。
而void func(int a)就不必改爲void func(const int &a);
三、 給返回值爲指針類型的函數加上const,會使函數返回值不能被修改,賦給的變量也只能是const型變量。如:函數const char*GetString(void); char *str=GetString()將會出錯。而const char *str=GetString()將是正確的。
四、 Const成員函數是指此函數體內只能調用Const成員變量,提升程序的鍵壯性。如聲明函數 int GetCount(void) const;此函數體內就只能調用Const成員變量。
Virtual:虛函數:派生類能夠覆蓋掉的函數,純虛函數:只是個空函數,沒有函數實現體;
十6、extern「C」有什麼做用?
Extern 「C」是由C++提供的一個鏈接交換指定符號,用於告訴C++這段代碼是C函數。這是由於C++編譯後庫中函數名會變得很長,與C生成的不一致,形成C++不能直接調用C函數,加上extren 「c」後,C++就能直接調用C函數了。
Extern 「C」主要使用正規DLL函數的引用和導出 和 在C++包含C函數或C頭文件時使用。使用時在前面加上extern 「c」 關鍵字便可。
十7、構造函數與析構函數
派生類的構造函數應在初始化表裏調用基類的構造函數;
派生類和基類的析構函數應加Virtual關鍵字。
不要小看構造函數和析構函數,其實編起來仍是不容易。
#include
class Base
{
public:
virtua ~Base() { cout<< "~Base" << end ; }
};
class Derived : public Base
{
public:
virtua ~Derived() { cout<< "~Derived" << end ; }
};
void main(void)
{
Base * pB = new Derived; // upcast
delete pB;
}
輸出結果爲:
~Derived
~Base
若是析構函數不爲虛,那麼輸出結果爲
~Base
十8、#IFNDEF/#DEFINE/#ENDIF有什麼做用
仿止該頭文件被重複引用面試
C++知識點系列之二
1. 寫一個"標準"宏MIN ,這個宏輸入兩個參數並返回較小的一個。
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
這個測試是爲下面的目的而設的:
1) 標識#define在宏中應用的基本知識。這是很重要的。由於在 嵌入(inline)操做符變爲標準C的一部分以前,宏是方便產生嵌入代碼的惟一方法,對於嵌入式系統來講,爲了能達到要求的性能,嵌入代碼常常是必須的方法。
2)三重條件操做符的知識。這個操做符存在C語言中的緣由是它使得編譯器能產生比if-then-else更優化的代碼,瞭解這個用法是很重要的。
3) 懂得在宏中當心地把參數用括號括起來
4) 我也用這個問題開始討論宏的反作用,例如:當你寫下面的代碼時會發生什麼事?
least = MIN(*p++, b);
2.嵌入式系統中常常要用到無限循環,你怎麼樣用C編寫死循環呢?
這個問題用幾個解決方案。我首選的方案是:
while(1)
{
}
一些程序員更喜歡以下方案:
for(;;)
{
}
這個實現方式讓我爲難,由於這個語法沒有確切表達到底怎麼回事。若是一個應試者給出這個做爲方案,我將用這個做爲一個機會去探究他們這樣作的基本原理。若是他們的基本答案是:"我被教着這樣作,但從沒有想到過爲何。"這會給我留下一個壞印象。
第三個方案是用 goto
Loop:
...
goto Loop;
應試者如給出上面的方案,這說明或者他是一個彙編語言程序員(這也許是好事)或者他是一個想進入新領域的BASIC/FORTRAN程序員。
3.
a) 一個整型數(An integer)
b)一個指向整型數的指針( A pointer to an integer)
c)一個指向指針的的指針,它指向的指針是指向一個整型數( A pointer to a pointer to an intege)r
d)一個有10個整型數的數組( An array of 10 integers)
e) 一個有10個指針的數組,該指針是指向一個整型數的。(An array of 10 pointers to integers)
f) 一個指向有10個整型數數組的指針( A pointer to an array of 10 integers)
g) 一個指向函數的指針,該函數有一個整型參數並返回一個整型數(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數並返回一個整型數( An array of ten pointers to functions that take an integer argument and return an integer )
答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
人們常常聲稱這裏有幾個問題是那種要翻一下書才能回答的問題,我贊成這種說法。當我寫這篇文章時,爲了肯定語法的正確性,個人確查了一下書。可是當我被面試的時候,我指望被問到這個問題(或者相近的問題)。由於在被面試的這段時間裏,我肯定我知道這個問題的答案。應試者若是不知道全部的答案(或至少大部分答案),那麼也就沒有爲此次面試作準備,若是該面試者沒有爲此次面試作準備,那麼他又能爲何出準備呢
4.
關鍵字static的做用是什麼?
這個簡單的問題不多有人能回答徹底。在C語言中,關鍵字static有三個明顯的做用:
1)在函數體,一個被聲明爲靜態的變量在這一函數被調用過程當中維持其值不變。
2) 在模塊內(但在函數體外),一個被聲明爲靜態的變量能夠被模塊內所用函數訪問,但不能被模塊外其它函數訪問。它是一個本地的全局變量。
3) 在模塊內,一個被聲明爲靜態的函數只可被這一模塊內的其它函數調用。那就是,這個函數被限制在聲明它的模塊的本地範圍內使用。
大多數應試者能正確回答第一部分,一部分能正確回答第二部分,同是不多的人能懂得第三部分。這是一個應試者的嚴重的缺點,由於他顯然不懂得本地化數據和代碼範圍的好處和重要性。
5. 關鍵字const有什麼含意?
我只要一聽到被面試者說:"const意味着常數",我就知道我正在和一個業餘者打交道。去年Dan Saks已經在他的文章裏徹底歸納了const的全部用法,所以ESP(譯者:Embedded Systems Programming)的每一位讀者應該很是熟悉const能作什麼和不能作什麼.若是你從沒有讀到那篇文章,只要能說出const意味着"只讀"就能夠了。儘管這個答案不是徹底的答案,但我接受它做爲一個正確的答案。(若是你想知道更詳細的答案,仔細讀一下Saks的文章吧。)
若是應試者能正確回答這個問題,我將問他一個附加的問題:
下面的聲明都是什麼意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前兩個的做用是同樣,a是一個常整型數。第三個意味着a是一個指向常整型數的指針(也就是,整型數是不可修改的,但指針能夠)。第四個意思a是一個指向整型數的常指針(也就是說,指針指向的整型數是能夠修改的,但指針是不可修改的)。最後一個意味着a是一個指向常整型數的常指針(也就是說,指針指向的整型數是不可修改的,同時指針也是不可修改的)。若是應試者能正確回答這些問題,那麼他就給我留下了一個好印象。順帶提一句,也許你可能會問,即便不用關鍵字 const,也仍是能很容易寫出功能正確的程序,那麼我爲何還要如此看重關鍵字const呢?我也以下的幾下理由:
1) 關鍵字const的做用是爲給讀你代碼的人傳達很是有用的信息,實際上,聲明一個參數爲常量是爲了告訴了用戶這個參數的應用目的。若是你曾花不少時間清理其它人留下的垃圾,你就會很快學會感謝這點多餘的信息。(固然,懂得用const的程序員不多會留下的垃圾讓別人來清理的。)
2) 經過給優化器一些附加的信息,使用關鍵字const也許能產生更緊湊的代碼。
3) 合理地使用關鍵字const可使編譯器很天然地保護那些不但願被改變的參數,防止其被無心的代碼修改。簡而言之,這樣能夠減小bug的出現。
6. 關鍵字volatile有什麼含意?並給出三個不一樣的例子。
一個定義爲volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優化器在用到這個變量時必須每次都當心地從新讀取這個變量的值,而不是使用保存在寄存器裏的備份。下面是volatile變量的幾個例子:
1) 並行設備的硬件寄存器(如:狀態寄存器)
2) 一箇中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
3) 多線程應用中被幾個任務共享的變量
回答不出這個問題的人是不會被僱傭的。我認爲這是區分C程序員和嵌入式系統程序員的最基本的問題。搞嵌入式的傢伙們常常同硬件、中斷、RTOS等等打交道,全部這些都要求用到volatile變量。不懂得volatile的內容將會帶來災難。
假設被面試者正確地回答了這是問題(嗯,懷疑是否會是這樣),我將稍微深究一下,看一下這傢伙是否是直正懂得volatile徹底的重要性。
1)一個參數既能夠是const還能夠是volatile嗎?解釋爲何。
2); 一個指針能夠是volatile 嗎?解釋爲何。
3); 下面的函數有什麼錯誤:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1)是的。一個例子是隻讀的狀態寄存器。它是volatile由於它可能被意想不到地改變。它是const由於程序不該該試圖去修改它。
2); 是的。儘管這並不很常見。一個例子是當一箇中服務子程序修該一個指向一個buffer的指針時。
3) 這段代碼有點變態。這段代碼的目的是用來返指針*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;
}
7.位操做(Bit manipulation)
下面的代碼輸出是什麼,爲何?
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}
這個問題測試你是否懂得C語言中的整數自動轉換原則,我發現有些開發者懂得極少這些東西。無論如何,這無符號整型問題的答案是輸出是 ">6"。緣由是當表達式中存在有符號類型和無符號類型時全部的操做數都自動轉換爲無符號類型。所以-20變成了一個很是大的正整數,因此該表達式計算出的結果大於6。這一點對於應當頻繁用到無符號數據類型的嵌入式系統來講是豐常重要的。若是你答錯了這個問題,你也就到了得不到這份工做的邊緣。
8. Typedef 在C語言中頻繁用以聲明一個已經存在的數據類型的同義字。也能夠用預處理器作相似的事。例如,思考一下下面的例子:
#define dPS struct s *
typedef struct s * tPS;
以上兩種狀況的意圖都是要定義dPS 和 tPS 做爲一個指向結構s指針。哪一種方法更好呢?(若是有的話)爲何?
這是一個很是微妙的問題,任何人答對這個問題(正當的緣由)是應當被恭喜的。答案是:typedef更好。思考下面的例子:
dPS p1,p2;
tPS p3,p4;
第一個擴展爲
struct s * p1, p2;
.
上面的代碼定義p1爲一個指向結構的指,p2爲一個實際的結構,這也許不是你想要的。第二個例子正確地定義了p3 和p4 兩個指針。晦澀的語法
9. C++中什麼數據分配在棧或堆中,New分配數據是在近堆仍是遠堆中?
答:棧: 存放局部變量,函數調用參數,函數返回值,函數返回地址。由系統管理
堆: 程序運行時動態申請,new 和 malloc申請的內存就在堆上編程
C++知識點系列之三數組
關於預處理
#define的常量用const取代,宏用inline函數取代。
在類定義中定義的函數自動爲內聯函數。也可使用inline修飾函數使其成爲內聯的。inline只有在函數定義時纔會發揮做用。所以頭文件中的內聯函數都是有函數體的。inline的函數也是內部聯接的。
friend的函數也能夠是inline的。
inline只是對編譯器的優化提示,編譯器不必定會對全部的inline進行內聯。
不可替代的預處理功能:
字符串化(#,將標識符轉化爲字符串):#define DEBUG(x) cout << #x " = " << x << endl
記號粘貼(##,鏈接兩個標識符造成一個新的標識符):#define FIELD(a) char* a##_string; int a##_size
static
編譯器保證對static的內置類型賦初值。對於static的對象調用其默認的構造函數。
static對象的析構函數會在main()函數結束或調用exit()時被自動調用。所以在static對象的析構函數中調用exit()可能會致使遞歸調用。調用abort()時不會自動調用static對象的析構函數。
全局的對象是static的。所以全局對象的構造和析構函數能夠用來在進入main()以前和退出main()以後完成一些工做。
對於全局的對象來講,由於其自己是靜態存儲的,所以使用static修飾的效果只是使其變爲內部聯接。
靜態數據成員的定義:
class A {
static int i;
public:
//...
};
int A::i = 1;
只有static const的整型才能在類內部被初始化,其餘類型都必須在外部(文件域中)被初始化。所以本地類(在函數中定義的類)中不能定義靜態成員。
static的成員函數在類中定義。爲全部該類的對象共用,調用時能夠直接使用類名::函數名。
static的成員函數只能訪問static的成員,由於static的成員沒有this。
static初始化的依賴關係(最好避免)。可參考iostream的實現(cin、cout和cerr都是在不一樣文件中的static對象)或另外一種(竟然無名!)方法。
namespace
#include <iostream.h>
意味着
#include <iostream>
using namespace std;
由於在標準化以前不存在名字空間,.h中的全部內容都是暴露的
namespace定義只能出如今全局域或另外一個namespace中。定義的{}後沒有分號。能夠跨越多個文件定義(不算重複定義)。也能夠定義別名。如:
namespace Bob = BobsSuperDuperLibrary;
每一個編譯單元都有一個未命名的namespace。經過將變量放入這個namespace中就能夠沒必要將它們聲明爲static的(內部聯接)。C++中要達到文件內的靜態就使用這種方法。
類中聲明的友元也會進入這個類所在的namespace。
using namespace只在當前域內有效(將namespace中的全部名稱注入當前域)。using namespace中引入的名稱能夠被覆蓋。
聯接方式。好比須要在C++中調用C的庫中的函數:
extern "C" float f(int a, char b);
不然的話聯接程序可能沒法解析函數調用。一般編譯器都會自動處理這種狀況。
引用和指針
引用必須在建立時被初始化爲引用某一個變量,而且一旦在初始化後就不能再改變被引用的對象。
函數返回引用時要注意引用的對象不能在函數返回後就不存在了,好比函數內部的變量。
拷貝構造函數接受的是本類的引用。定義拷貝構造函數就必須定義構造函數。
經過聲明一個private的拷貝構造函數能夠防止對象被傳值。
指向特定類中成員的指針:
聲明:
int ObjectClass::*pointerToMember;
初始化:
int ObjectClass::*pointerToMember = &ObjectClass::a;
使用:
objectPointer->*pointerToMember = 47;
object.*pointerToMember = 47;
對於成員函數也適用
運算符重載
重載的運算符必須接受至少一個自定義類型。接受的參數都爲內置類型的運算符沒法被重載。
運算符做爲類的成員函數被重載時,類的對象就做爲第一個參數。注意此時函數的返回方式。
重載++a會調用operator++(a),重載a++會調用operator++(a, int),其中第二個int參數是不會被用到的,只是用來區分前綴和後綴調用。--的重載也是同樣。
=和[]只能做爲成員函數被重載。()只能做爲成員函數被重載,能夠帶任意多個參數。(若能夠不做爲成員函數被重載,則對於內置類型的運算就能夠被重載,這是沒有意義的)
->和->*也只能做爲成員函數被重載,但對返回值有必定的限制。
.和.*不能被重載
返回值優化:
Integer tmp(left.i + right.i);
return tmp;
這樣編譯器須要三步才能完成(構造,拷貝,析構),而
return Integer(left.i + right.i);
則只須要一步
重載時做爲成員或非成員函數的選擇:
全部的一元運算符 推薦做爲成員
= () [] -> ->* 必須做爲成員
+= -= /= *= ^=
&= |= %= >>= <<= 推薦做爲成員
全部其餘的二元運算符 推薦做爲非成員
當對象尚未被建立時,=調用的是構造函數或拷貝構造函數,爲的是初始化對象;當對象已被建立時,=調用的纔是operator=。所以
Fee fee(1);
Fee fum(fi);
這樣的寫法要比
Fee fee = 1;
Fee fum = fi;
這樣的寫法清晰。
在重載賦值運算符時,首先檢查是不是對自身賦值是個好習慣。
當對象中有指針時,拷貝構造函數一般須要連同複製出指針所指向的內容。而當此內容很大時,一般採用引用計數的方法,只在須要修改數據且引用數大於1時才複製內容。這種技術被稱爲copy-on-write。
當構造函數接受一個其餘類型的對象做爲參數時,編譯器能夠用它來進行自動類型轉換。若是不須要這樣的自動轉換,在構造函數前加上explicit。
也能夠重載operator 類型名來定義自動類型轉換。因爲這種類型轉換是由源對象完成的(不像構造函數的類型轉換是由目標對象完成的),所以能夠完成自定義對象到內置類型的轉換。
運算符重載爲非成員函數時,運算符兩邊均可以進行自動類型轉換。
提供自動類型轉換時注意兩種類型之間只需提供一條轉換路徑,不然會出現二義性錯誤。
new和delete
new爲對象分配空間並調用構造函數。delete調用對象的析構函數後釋放對象的空間。
delete對於零指針無效,所以人們一般在delete以後將指針設爲零,以防止屢次delete帶來的問題。
delete void*可能會帶來問題。因爲編譯器不知道具體的類型,將致使對象的空間被釋放而沒有調用析構函數。這可能會引發內存泄漏。
a* p = new a[100];爲數組中的每一個對象分配空間並調用構造函數進行初始化。
delete []p;則完成相反的事。delete後的[]表示指針只是數組的首地址,編譯器會自動獲取數組的大小完成釋放工做。因爲表示首地址,最好定義爲常量:
a* const p = new a[100];
當new找不到空間分配時,會調用new-handler。其默認行爲是拋出異常。能夠經過使用new.h中的set_new_handler()來定義做爲new-handler的函數。
operator new和operator delete均可以被重載,用來完成一些自定義的內存管理功能:
void* operator new(size_t sz)
void operator delete(void* m)
當new和delete做爲類的成員函數被重載時,爲該類的對象分配空間時就會調用這些重載的操做符。
用於爲數組分配空間的operator new[]和operator delete[]也能被重載。實際佔用的空間比分配的要多4個字節,被系統用於存儲數組的大小。
重載的new能夠接受任意多個參數,好比定義第二個參數,用於指明在何處分配空間:
void* operator new(size_t, void* loc)
使用時:
X* xp = new(a) X;
其中的a就是第二個參數。採用這種形式的new可能須要顯式調用析構函數:
xp->X::~X();
(由於對象可能不是構建在堆上,使用delete只能釋放堆上的空間)
繼承
合成和繼承都須要在構造初始化列表中對子對象進行初始化。
一旦在子類中定義了一個與超類中同名的函數,超類中全部同名的函數,無論函數特徵是否相同,都會變得不可見。
構造函數,析構函數,operator=不會在繼承時進入子類。
繼承默認是private的,即超類中的public成員在子類中也會變成private的。若只想暴露某些成員,能夠在子類中的public部分使用using。這種狀況下同名的重載函數會被所有暴露。
多態
C++中使用virtual關鍵字來聲明函數爲延遲綁定的(或動態綁定)。函數的定義中不須要virtual。使用了virtual以後, upcast時調用的就是子類的重載函數,不然只能調用基類的。(C++使用virtual來使得動態綁定成爲可選的,這是出於效率的考慮。其餘語言如 Java和Python等默認就是使用動態綁定的)
擁有純虛函數的類是抽象類(提供了一個接口)。純虛函數在virtual定義後加上=0。
繼承抽象類的子類必須實現基類中全部的純虛函數,不然就仍是抽象類。
純虛函數不能內聯定義。在定義後則能夠被子類使用。
傳值的upcast會把對象切割,即子類會被切到只剩下基類的部分。抽象的基類能夠避免這種狀況,由於抽象類不容許實例化。
構造函數不能是virtual的,而析構函數能夠。
模板
繼承能夠重用對象,而模板能夠重用代碼。
定義template時可使用內置類型。它們的值能夠是常量。如:
template<class T, int size = 100>
template不只能夠用來建立類模板,還能用來建立函數模板。
雜項
之間沒有標點符號的字符數組會被自動鏈接起來
C++標準中包含的不是STL,而是C++標準庫,它是由STL演變來的。
C++中的類型轉換能夠函數的形式,如float(n)等價於(float)n。
C++中的顯示類型轉換(提供一種容易辨別的形式):
static_cast:類型收窄時能夠避免編譯器的警告信息;一樣可用於類型放寬、void*的強制轉換和自動隱式轉換。
const_cast:用於轉換const和volatile,好比將一個普通指針指向一個const
reinterpret_cast:把一種類型當成另外一種類型
dynamic_cast:用於downcast
sizeof是運算符而不是函數,用於類型時要加括號,如sizeof(double),用於變量時不用,如sizeof x。安全