C++PrimerPlus知識點小結
- C++融合了3種不同的程式設計方式:C語言代表的過程性語言,C++在C語言基礎上新增的類代表的面嚮物件語言(oop),C++模板支援的泛型程式設計。
- C++11初始化方式
//大括號初始化,等號可以使用,也可以不使用
int emus{7};
int rheas={12};
//大括號內不包含任何東西,這種情況下變數將被初始化為零
int rocs={};
int psychics{};
- C++儲存整型的方式類似一個圈,負數存的是補碼。可以手動模擬一下。
- 第一位為1-9,則基數為10(十進位制),第一位為0,則基數為8(8進位制),前兩位為0x或0X,則基數為16
- cout的一些特殊特性
/*
cout提供了控制符dec,hex,oct分別用於指示cout以十進位制,
十六進位制和八進位制格式顯示整數。
*/
int tmp=42;
cout<<tmp<<endl;
cout<<hex;
cout<<tmp<<endl;
cout<<oct;
cout<<tmp<<endl;
- C++11強制型別轉換運算子static_cast < typeName>(value)
//使用要求更為嚴格,雖然不知道有什麼用。。
char c='c';
cout<<static_cast<int>(c)<<endl;
- cin.get() cin會留下換行符 cin.getline 不會留下換行符
- C++11新增的另一種型別:原始(raw)字串
cout<<R"(Jim "King" Tutt uses "\n" instead of endl.)"<<'\n';
//輸出 Jim "King" Tutt uses \n instead of endl.
- C++結構體種的位欄位
C++允許指定佔用特定位數的結構成員,這使得建立與某個硬體裝置上的暫存器對應的資料結構非常方便。欄位的型別應為整型或者列舉,接下來是冒號,冒號後面跟著一個數字,它制定了位數。可以使用沒有名稱的欄位提供間距。每個成員都被稱為位欄位。
struct torgle_register
{
unsigned int SN : 4;
unsigned int : 4;
bool goodIn : 1;
bool goodTorgle : 1;
};
torgle_register tr = {14,true,false};
if(tr.goodIn)puts("Yes");
- 共用體
結構體可以同時儲存int,long和double,共用體只能儲存int,long或double
union one4all
{
int int_val;
long long_val;
double double_val;
};
one4all pail;
pail.int_val=15;
cout<<pail.int_val<<endl;
pail.double_val=1.38;
cout<<pail.double_val<<endl;
cout<<pail.int_val<<endl;
/*
輸出
15
1.38
-515396076
*/
由於共用體每次只能儲存一個值,因此它必須有足夠的空間來儲存最大的成員,所以,共用體的長度為其最大的成員的長度。
匿名共用體:
struct widget
{
char brand[20];
int type;
union
{
long id_num;
char id_char[20];
};
};
widget prize;
cin>>prize.type;
if(prize.type==1)cin>>prize.id_num;
else cin>>prize.id_char;
共用體常用於(但並非只能用於)節省記憶體。C++可用於嵌入式系統程式設計,如控制烤箱,MP3播放器或火星漫步者的處理器。對於這些應用程式來說,記憶體可能十分寶貴。另外,共用體常用於操作作業系統資料結構或硬體資料結構。
- sizeof運算子和指標,陣列之間的關係。
char *p="abc";
cout<<sizeof(p)<<endl;//指標長度,一律返回4
cout<<sizeof(*p)<<endl;//字元長度,1個位元組
char a[]="abcdef";
cout<<sizeof(a)<<endl;//陣列長度 7
cout<<sizeof(&a)<<endl;//指標長度,一律返回4
cout<<sizeof(*a)<<endl;//字元長度,1個位元組
- 型別組合(P118)
struct student
{
int year;
};
//變數
student a1,a2,a3;
a1.year=2003;
a2.year=2005;
//陣列
student t[3];
t[0].year=2003;
(t+1)->year=2004;
//指標陣列
const student *b[3]={&a1,&a2,&a3};
cout<<b[0]->year<<endl;
//陣列指標
const student **c=b;
cout<<(*c)->year<<endl;
cout<<c[0]->year<<endl;
- 基於範圍的for迴圈(C++11)(P152)
C++新增一種迴圈,基於範圍(range-based)的for迴圈。
double prices[5]={4.99,1.99,6.87,7.99,8.49};
for(double x:prices)
cout<<x<<endl;
//要修改陣列的元素,需要使用不同的迴圈變數語法
for(double &x:prices)
x=x*0.8;
//還可以使用基於範圍的for迴圈和初始化列表
for(int x:{3,5,2,8,6})
cout<<x<<' ';
cout<<endl;
- 檔案尾條件(P155)
輸入來自於檔案時,使用一些特殊符號來表示結束有時會很難令人滿意,這時我們可以通過檢測檔案尾(EOF)。
如果輸入來自於鍵盤呢,很多作業系統都允許通過鍵盤來模擬檔案尾條件。在Unix中,可以在行首按下Ctrl+D來實現。在Windows命令提示符模式下,可以在任意位置按Ctr+Z和Enter。
如果程式設計環境檢測到EOF後,cin將兩位(eofbit和failbit)都置為1.可以通過成員函式eof()來檢視eofbit是否被設定;如果監測到EOF,則cin.eof()將返回true,否則返回false。同樣,如果eofbit或failbit被置為1,則fail()成員函式返回true,否則返回false。
cin.fail()比cin.eof()更通用,因為它可以檢測到其他失敗原因,如磁碟故障。 - cin.get()有兩種用法,一種ch=cin.get(),一種cin.get(ch)。
- 寫入及讀取檔案
//寫入
ofstream outFile;
ofstream fout;
outFile.open("fish.txt");
char s[30];
scanf("%s",s);
fout.open(s);
outFile<<"hello world"<<endl;
outFile.close();
fout.close();
//讀取
ifstream inFile;
//ifstream fin;
inFile.open("fish.txt");
if(!inFile.is_open())
{
puts("Error");
exit(EXIT_FAILURE);
}
char s[30];
while(inFile>>s)
{
cout<<s<<endl;
}
inFile.close();
//fin.close();
/*
fish.txt
hello world
*/
- 使用陣列區間的函式(P220)
迭代器原型
int sum_arr(const int *begin,const int *end)
{
int tot=0;
for(const int *pt=begin;pt!=end;pt++)
tot=tot+*pt;
return tot;
}
int main()
{
sum[0]=1,sum[1]=2;sum[2]=3;
cout<<sum_arr(sum,sum+3);
}
- 指標與const(P222)
可以用兩種不同的方式將const關鍵字用於指標,第一種方法是讓指標指向一個常量物件,這樣可以防止使用指標來修改所指向的值,第二種方法是將指標本身宣告為常量,這樣可以防止改變指標指向的位置。下面來看細節。
int age=30;
const int *pt=&age;
*pt += 1;//invalid
const int age2=30;
int *p2=&age2;//invalid
pt的什麼並不意味著指向的值是一個常量,而只是意味著對pt而言,這個值是一個常量。我們可以通過age變數來修改age的值,但不能用pt指標來修改它。
第二種也不行。
如果指標指向指標,情況更復雜。
假設涉及的是一級間接關係,則將非const指標賦給const指標是可以的。
int age=39;
int *pd=&age;
const int *pt=pd;
但進入兩級間接關係時,與一級間接關係一樣將const和非const混合的指標賦值方式將不再安全,因此是不行的。如果允許這樣做,則可以編寫這樣的程式碼:
const int **pp2;
int *p1;
const int n=13;
pp2=&p1;
*pp2=&n;
*p1=10;
上述程式碼將非const地址(&p1)賦給了const指標pp2,因此可以使用p1來修改const資料。因此,這樣是不行的。
如果資料型別本身並不是指標,則可以將const資料或非const資料的地址賦給指向const的指標,但只能將非const資料的地址賦給非const指標。
- 函式指標
宣告時函式指標的返回型別以及引數列表和函式必須一樣。
double pam(int);
double (*pf)(int);
int main()
{
pf=pam;
double x=pam(4);
double y=(*pf)(5);
cout<<y<<endl;
y=pf(5);//這樣寫也行
cout<<y<<endl;
}
double pam(int a)
{
return 1.0*a*a;
}
深入探討函式指標
假如現在有下面一些函式的原型,他們的引數列表和返回值型別相同:
const double *f1(const double ar[],int n);
const double *f2(const double [],int n);
const double *f3(const double *,int);
接下來,假設要宣告一個指標,它可指向這三個函式之一。假定該指標名為p1:
const double *(*p1)(const double *,int);
p1=f1;
//還可以這樣寫
auto p2=f2;
現在來看下面的語句:
cout<<p1(a,3)<<' '<<*(p1(a,3))<<endl;
cout<<p2(a,3)<<' '<<*(p2(a,3))<<endl;
//前面返回的是地址,後面返回的是值
如果有一個函式指標陣列,包含這三個函式,該怎麼宣告呢?
const double * (*pa[3])(const double *,int)={f1,f2,f3}
這裡要說明一點,運算子[]的優先順序高於*,其他的可以自己理解。
如果要宣告一個函式指標陣列指標,用來指向pa陣列,該怎麼宣告呢?
//第一種
auto pc=&pa;
//第二種 pd是一個指標,它指向一個包含三個元素的陣列。
const double *(*(*pd)[3])(const double *,int)=&pa;
(有點沒弄清楚)這樣的宣告方式應該是陣列指標的宣告。
要呼叫函式,需認識到這一點,既然pd指向陣列,那麼*pd就是陣列,而(*pd)[i]就是陣列中的元素,即函式指標。因此,較簡單的函式呼叫是(*pd)[i](av,3),而
是返回的指標指向的值。也可以使用第二種使用指標呼叫函式的語法,使用
來呼叫函式,而
是指向的double的值。
請注意pa(它是陣列名,表示地址)和&pa之間的差別。pa都是陣列第一個元素的地址,即&pa[0]。因此,它是單個指標的地址。但&pa是整個陣列(即三個指標塊)的地址。從數字上說,pa和&pa的值相同,但它們的型別不同。一種差別是,pa+1為陣列中下一個元素的地址,而&pa+1為陣列pa後面一個12位元組記憶體塊的地址(假定一個地址為4個位元組)。另一個差別是,要得到第一個元素的值,只需對pa解除一次引用,但需要對&pa解除兩次引用:
使用typedef 進行簡化
typedef 放在型別宣告前,在變數的位置填上新型別的名字,這樣新型別就可以當舊型別用,換句話說,宣告變數時,在句首加上typedef,就相當於聲明瞭新型別。
typedef const double *(*p_fun)(const double *,int);
p_fun p1=f1;
p_fun pa[3]={f1,f2,f3};
p_fun (*pd)[3]=&pa;
-
C++提供了許多新的函式特性,包括行內函數,按引用傳遞變數,預設的引數值,函式過載(多型)以及模板函式。
-
行內函數
行內函數的編譯程式碼與其他的程式程式碼“內聯”到一塊,對於內聯程式碼,程式無需跳到另一個位置處執行程式碼,再跳回來。因此,行內函數的執行速度比常規函式稍快,但代價是需要佔用更多記憶體。
應有選擇的使用行內函數。如果執行函式程式碼的時間比處理函式呼叫機制的時間長,則節省的時間只佔一小部分。如果程式碼執行時間很短,則內聯呼叫就可以節省非內聯呼叫使用的大部分時間。 -
臨時變數,引用引數和const(P262)
如果引用引數是const,則編譯器將在下面兩種情況下生成臨時變數:- 實參的型別正確,但不是左值。
- 實參的型別不正確,但可以轉換成正確的型別。
左值:左值引數是可被引用的資料物件,例如,變數,陣列元素,結構成員,引用和解除引用的指標都是左值。非左值包括字面常量(用引號括起的字串除外,它們由其地址表示)和包含多項的表示式。在C語言中,左值最初指的是可出現在賦值語句左邊的實體,但這是引入關鍵字const之前的情況。現在,常規變數和const變數都可視為左值,因為可通過地址訪問他們。但常規變數屬於可修改的左值,而const變數屬於不可修改的左值。
簡而言之,如果接受引用引數的函式的意圖是修改作為引數傳遞的變數,則建立臨時變數將阻止這種意圖的實現。解決方法是,禁止這麼做。現在C++標準正是這樣。
如果只是使用傳遞的值,而不是修改它們,因此臨時變數不會造成任何不利的影響,反而會使函式在可處理的引數種類方面更通用。 -
繼承的一個特徵是,派生類繼承了基類的方法(如ofstream繼承了ostream,ofstream可以使用ostream的一些方法),另一個特徵是,基類引用可以指向派生類物件,而無需進行強制型別轉換。
-
C++如何跟蹤每一個過載函式呢?C++編譯器將執行一些神奇的操作-名稱修飾或名稱矯正。它會根據函式原型中指定的形參型別對每個函式名進行加密。