C++教程(超長(zhǎng)最全入門(mén))
1、C++簡(jiǎn)介
1.1 起源
-貝爾實(shí)驗(yàn)室20世紀(jì)80年代(1979)
1.2 應(yīng)用范圍
- 文字處理程序以及電子表格
- 編譯器
- 操作系統(tǒng)
- 大型游戲等
1.3 C++和C
- C語(yǔ)言是結(jié)構(gòu)化和模塊化的語(yǔ)言,面向過(guò)程。
- C++保留了C語(yǔ)言原有的所有優(yōu)點(diǎn),增加了面向?qū)ο蟮臋C(jī)制,俗稱(chēng)“帶類(lèi)的C",1983年更名為C++
2、開(kāi)發(fā)工具
- 記事本(Notepad++)+命令行
- Visual C++ 6.0:經(jīng)典開(kāi)發(fā)工具,與流行操作系統(tǒng)有沖突
- VS 2015等:功能強(qiáng)大,體積也強(qiáng)大
- Code::Blocks:開(kāi)源免費(fèi)開(kāi)發(fā)工具,專(zhuān)業(yè)開(kāi)發(fā)人員推薦使用
- 其他開(kāi)發(fā)工具:DeV C++、CLion、C-Free、Xcode、C4droid
3、基本語(yǔ)法
- 對(duì)象-對(duì)象具有狀態(tài)的行為。對(duì)象是類(lèi)的實(shí)例。
- 類(lèi)-類(lèi)可以定義為對(duì)象行為、狀態(tài)的模版。
- 方法-從基本上講,一個(gè)方法表示一種行為,一個(gè)類(lèi)可以包含多種方法。
- 變量
3.1 注釋
//單行注釋 /* 多行注釋 多行注釋 */
3.2關(guān)鍵字
| asm | else | new | this |
|---|---|---|---|
| auto | enum | operator | throw |
| bool | explicit | private | true |
| break | export | protected | try |
| case | extern | public | typedef |
| catch | false | register | typeid |
| char | float | reinterpret_cast | typename |
| class | for | return | union |
| const | friend | short | unsigned |
| const_cast | goto | signed | using |
| continue | if | sizeof | virtual |
| default | inline | static | void |
| delete | int | static_cast | volatile |
| do | long | struct | wchar_t |
| double | mutable | switch | while |
| dynamic_cast | namespace | template |
3.3標(biāo)識(shí)符
- 標(biāo)識(shí)符是用來(lái)標(biāo)識(shí)變量、函數(shù)、類(lèi)、模塊,或任何其他用戶(hù)自定義項(xiàng)目的名稱(chēng)。一個(gè)標(biāo)識(shí)符以字母 A-Z 或 a-z 或下劃線 _ 開(kāi)始,后跟零個(gè)或多個(gè)字母、下劃線和數(shù)字(0-9)。
- 標(biāo)識(shí)符內(nèi)不允許出現(xiàn)標(biāo)點(diǎn)字符,比如 @、& 和 %。C++ 是區(qū)分大小寫(xiě)的編程語(yǔ)言。
4、數(shù)據(jù)類(lèi)型
4.1基本數(shù)據(jù)類(lèi)型
七種基本的C++數(shù)據(jù)類(lèi)型:bool、char、int、float、double、void、wchar_t
類(lèi)型修飾符:signed、unsigned、short、long
注:一些基本類(lèi)型可以使用一個(gè)或多個(gè)類(lèi)型修飾符進(jìn)行修飾,比如:signed short int簡(jiǎn)寫(xiě)為short、signed long int 簡(jiǎn)寫(xiě)為long。
| 類(lèi)型名 | 占用字節(jié)數(shù) | 數(shù)值范圍 |
|---|---|---|
| void | 0 | |
| bool | 1 | {true.false} |
| wchar_t | 2或4個(gè)字節(jié) | |
| char(signed char) | 1 | -128~+127 |
| short(signed short) | 2 | -32768~+32767 |
| int(signed int) | 4 | -2147483648~+2147483647 |
| long(signed long) | 4 | -2147483648~+2147483647 |
| long long(signed long long) | 8 | -9,223,372,036,854,775,808 ~9,223,372,036,854,775,807 |
| float | 4 | -.34*1038~3.4*1038 |
| double | 8 | -1.7*10308~1.7*10308 |
| unsigned char | 1 | 0~255 |
| unsigned shrot | 2 | 0~65525 |
| unsigned(unsigned int) | 4 | 0~4294967295 |
| unsigned long | 4 | 0~4294967295 |
| unsigned long long | 8 | 0 ~ 18,446,744,073,709,551,615 |
//x64處理器 64位window10 vs2015
#include <iostream>
using namespace std;
int main()
{
bool b;
char c;short s; int i; long l; long long ll; float f; double d; long double ld;long float lf;
unsigned char uc; unsigned short us; unsigned int ui; unsigned long ul; unsigned long long ull;
cout << sizeof(bool) << endl;
cout << sizeof(char)<<" " << sizeof(short)<<" "<< sizeof(signed int) << " " << sizeof(long) << " " << sizeof(signed long long) << " " << sizeof(float) << " " << sizeof(double) << " " << sizeof(long float) << " " << sizeof(long double) << endl;
cout <<sizeof(unsigned char)<<" "<< sizeof(unsigned short) << " " << sizeof(unsigned int) << " " << sizeof(unsigned long) << " " << sizeof(unsigned long long) << endl;
cout << sizeof(unsigned) << endl;
cout << "hello World!!!" <<endl;
system("pause");
return 0;
}
4.2 數(shù)據(jù)類(lèi)型在不同系統(tǒng)中所占空間大小
這個(gè)與機(jī)器、操作系統(tǒng)、編譯器有關(guān)。比如同樣是在32bits的操作系統(tǒng)系,VC++的編譯器下int類(lèi)型為占4個(gè)字節(jié);而tuborC下則是2個(gè)字節(jié)。
原因:
- c/c++規(guī)定int字長(zhǎng)和機(jī)器字長(zhǎng)相同
- 操作系統(tǒng)字長(zhǎng)和機(jī)器字長(zhǎng)未必一致
- 編譯器根據(jù)操作系統(tǒng)字長(zhǎng)來(lái)定義int字長(zhǎng)
| 類(lèi)型 | 16位操作系統(tǒng) | 32位操作系統(tǒng) | 64位操作系統(tǒng) |
|---|---|---|---|
| char | 1 | 1 | 1 |
| char* | 2 | 4 | 8 |
| short | 2 | 2 | 2 |
| int | 2 | 4 | 4 |
| long | 4 | 4 | 8 |
| long long | 8 | 8 | 8 |
注:long類(lèi)型在不同編譯器中的占位不一樣: 32位時(shí),VC++和GCC都是4字節(jié); 64位時(shí),VC++是4字節(jié),GCC是8字節(jié)。
4.3 typedef聲明
//使用typedef為一個(gè)已有的類(lèi)型取一個(gè)新的名字,語(yǔ)法如下: typedef type newname //eg: typedef int feet feet distance
4.4 枚舉類(lèi)型
C++中的一種派生數(shù)據(jù)類(lèi)型,它是由用戶(hù)定義的若干枚舉常量的集合;枚舉元素是一個(gè)整型,枚舉型可以隱式的轉(zhuǎn)換為int型,int型不能隱式的轉(zhuǎn)換為枚舉型。
//枚舉類(lèi)型的語(yǔ)法:
enum 枚舉名{
標(biāo)識(shí)符[=整型常數(shù)],
標(biāo)識(shí)符[=整型常數(shù)],
...
標(biāo)識(shí)符[=整型常數(shù)]
}枚舉變量;如果枚舉沒(méi)有初始化, 即省掉"=整型常數(shù)"時(shí), 則從第一個(gè)標(biāo)識(shí)符開(kāi)始;
默認(rèn)情況下,第一個(gè)名稱(chēng)的值為 0,第二個(gè)名稱(chēng)的值為 1,第三個(gè)名稱(chēng)的值為 2,以此類(lèi)推。但是,您也可以給名稱(chēng)賦予一個(gè)特殊的值,只需要添加一個(gè)初始值即可。
例如:
enum course {math,chinese,english,physics,chemistry}c;
c = english;
cout<<c<<endl; //2
//english為1 physics為2 chemistry為3,chinese仍為1,math仍為0
enum course {math,chinese,english=1,physics,chemistry};5、變量
變量其實(shí)只不過(guò)是程序可操作的存儲(chǔ)區(qū)的名稱(chēng)。C++ 中每個(gè)變量都有指定的類(lèi)型,類(lèi)型決定了變量存儲(chǔ)的大小和布局,該范圍內(nèi)的值都可以存儲(chǔ)在內(nèi)存中,運(yùn)算符可應(yīng)用于變量上。
5.1 變量的聲明和定義
- 變量聲明向編譯器保證變量以給定的類(lèi)型和名稱(chēng)存在,這樣編譯器在不需要知道變量完整細(xì)節(jié)的情況下也能繼續(xù)進(jìn)一步的編譯。
- 可以在 C++ 程序中多次聲明一個(gè)變量,但變量只能在某個(gè)文件、函數(shù)或代碼塊中被定義一次。
- 多個(gè)變量賦同一個(gè)值時(shí),需要分別賦值。
int x = y = z = 66;//錯(cuò)誤 int x = 3,y = 3,z = 3; int x, y ,z = 3; x = y = z;
變量的聲明(不分配內(nèi)存):extern 數(shù)據(jù)類(lèi)型 變量名;
變量的定義:數(shù)據(jù)類(lèi)型 變量名1,變量名2,...變量名n;
// 變量聲明
extern int a, b;
int main ()
{
// 變量定義
int a, b;
// 初始化
a = 23;
b = 25;
return 0;
}5.2 變量的作用域
局部變量:在函數(shù)或一個(gè)代碼塊內(nèi)部聲明的變量,稱(chēng)為局部變量。它們只能被函數(shù)內(nèi)部或者代碼塊內(nèi)部的語(yǔ)句使用。
全局變量:在所有函數(shù)外部定義的變量(通常是在程序的頭部),稱(chēng)為全局變量。全局變量的值在程序的整個(gè)生命周期內(nèi)都是有效的。
- 局部變量和全局變量的名稱(chēng)可以相同,但是在函數(shù)內(nèi),局部變量的值會(huì)覆蓋全局變量的值。
- 當(dāng)局部變量被定義時(shí),系統(tǒng)不會(huì)對(duì)其初始化;定義全局變量時(shí),系統(tǒng)會(huì)自動(dòng)初始化值:
int float double 0,char ’\0‘,指針 NULL
int i = 66;
int main ()
{
int i = 88;
cout << i<<endl;//8
return 0;
}
float f;
double d;
char c;
int *p;
int main()
{
cout << i << f << d << c << p << endl;//000 00000000
return 0
}6、運(yùn)算符
- 算術(shù)運(yùn)算符:
+ - * / % ++ -- - 關(guān)系運(yùn)算符:
== != < > >= <= - 邏輯運(yùn)算符:
&& || ! - 位運(yùn)算符:
& | ^ ~ << >> - 賦值運(yùn)算符:
= += -= *= /= %= <<= >>= &= ^= != - 雜項(xiàng)運(yùn)算符:
sizeof //返回變量的大小,eg:sizeof(a)返回4 a是整型 sizeof(int) Condition?X:Y //三元運(yùn)算符 Condition為true,值為X,否則值為Y , //逗號(hào)表達(dá)式,值為最后一個(gè)表達(dá)式的值 .和-> //用于引用類(lèi)、結(jié)構(gòu)和公用體的成員 Cast //強(qiáng)制類(lèi)型轉(zhuǎn)換符 eg:int(2.202)返回2 & //指針運(yùn)算符 返回變量的地址 * //指針運(yùn)算符 指向一個(gè)變量
運(yùn)算符優(yōu)先級(jí)
| 類(lèi)別 | 運(yùn)算符 | 結(jié)合性 |
|---|---|---|
| 后綴 | () [] -> . ++ - - | 從左到右 |
| 一元 | + - ! ~ ++ - - (type)* & sizeof | 從右到左 |
| 乘除 | * / % | 從左到右 |
| 加減 | + - | 從左到右 |
| 移位 | << >> | 從左到右 |
| 關(guān)系 | < <= > >= | 從左到右 |
| 相等 | == != | 從左到右 |
| 位與 AND | & | 從左到右 |
| 位異或 XOR | ^ | 從左到右 |
| 位或 OR | | | 從左到右 |
| 邏輯與 AND | && | 從左到右 |
| 邏輯或 OR | || | 從左到右 |
| 條件 | ?: | 從右到左 |
| 賦值 | = += -= *= /= %=>>= <<= &= ^= | = |
| 逗號(hào) | , | 從左到右 |
7、語(yǔ)法結(jié)構(gòu)
7.1 循環(huán)結(jié)構(gòu)
while
while(conditon)//0為false,非0為true
{
statement(s);
}for
for(init;conditon;increment)//0為false,非0或什么也不寫(xiě)為true
{
statement(s);
}1.init首先被執(zhí),且只會(huì)執(zhí)行一次,也可以不寫(xiě)任何語(yǔ)句。
2.然后會(huì)判斷conditon,true執(zhí)行循環(huán)主體,false跳過(guò)循環(huán)
3.執(zhí)行完循環(huán)主體,執(zhí)行increment,跳到2
int array[5] = { 11, 22, 33, 44, 55 };
for (int x : array)
{
cout << x << " ";
}
cout << endl;
// auto 類(lèi)型也是 C++11 新標(biāo)準(zhǔn)中的,用來(lái)自動(dòng)獲取變量的類(lèi)型
for (auto x : array)
{
cout << x << " ";
}for each
STL中的for增強(qiáng)循環(huán)。
int a[4] = { 4,3,2,1 };
for each (int var in a)
{
cout << var << " ";
}7.2 判斷結(jié)構(gòu)
if
if(expr)
{
statement;//如果expr為true將執(zhí)行的語(yǔ)句塊
}
if(expr)
{
statement1;// 如果expr為true將執(zhí)行的語(yǔ)句塊
}
else
{
statement2;// 如果expr為false將執(zhí)行的語(yǔ)句
}
if(expr1)
{
statement1;// 如果expr1為true將執(zhí)行的語(yǔ)句塊
}
elseif(expr2)
{
statement2;// 如果expr2為true將執(zhí)行的語(yǔ)句塊
}
...
else
{
statementElse;// 當(dāng)上面的表達(dá)式都為false執(zhí)行的語(yǔ)句塊
}switch
switch(expression){
case constant-expression :
statement(s);
break;
case constant-expression :
statement(s);
break;
// 您可以有任意數(shù)量的 case 語(yǔ)句
default : // 可選的
statement(s);
}- 每個(gè)case后滿(mǎn)的常量表達(dá)式必須各不相同。
- case語(yǔ)句和default語(yǔ)句出現(xiàn)的順序?qū)?zhí)行結(jié)果沒(méi)有影響。
- 若case后沒(méi)有break,執(zhí)行完就不會(huì)判斷,繼續(xù)執(zhí)行下一個(gè)case語(yǔ)句。直到遇到brerak。
- default后面如果沒(méi)有case,則break可以省略
- 多個(gè)case可以用一組執(zhí)行語(yǔ)句
char c = 'A';
switch (c)
{
case 'A':
case 'B':
case 'C':
cout << "及格了" << endl;
break;
default:
cout << "不及格" << endl;
}7.3 三元運(yùn)算符
//如果 Exp1 為真,則計(jì)算 Exp2 的值,結(jié)果即為整個(gè) ? 表達(dá)式的值。如果 Exp1 為假,則計(jì)算 Exp3 的值,結(jié)果即為整個(gè) ? 表達(dá)式的值 Exp1 ? Exp2 : Exp3;
7.4 預(yù)處理命令
預(yù)處理程序(刪除程序注釋?zhuān)瑘?zhí)行預(yù)處理命令等)–>編譯器編譯源程序
- 宏定義:
#define 標(biāo)識(shí)符 字符串 - 文件包含:
#include<filename> 或者#include“filename” - 條件編譯
//如果標(biāo)識(shí)符被#define定義過(guò),執(zhí)行程序段1,否則執(zhí)行程序段2 #ifdef 標(biāo)識(shí)符 程序段1 #else 程序段2 #endif //如果標(biāo)識(shí)符沒(méi)有被#define定義過(guò),執(zhí)行程序段1,否則執(zhí)行程序段2 #ifndef 標(biāo)識(shí)符 程序段1 #else 程序段2 #endif //如果表達(dá)式為true,執(zhí)行程序段1,否則執(zhí)行程序段2 #if 表達(dá)式 程序段1 #else 程序段2 #endif
8、數(shù)組
一些具有相同數(shù)據(jù)類(lèi)型或相同屬性(類(lèi))的數(shù)據(jù)的集合,用數(shù)據(jù)名標(biāo)識(shí),用下標(biāo)或序號(hào)區(qū)分各個(gè)數(shù)據(jù)。數(shù)組中的數(shù)據(jù)稱(chēng)為元素。
8.1一維數(shù)組
定義一維數(shù)組的形式:數(shù)據(jù)類(lèi)型 數(shù)據(jù)名[常量表達(dá)式]
初始化的形式:數(shù)據(jù)類(lèi)型 數(shù)組名[常量表達(dá)式] = {初值表};
為數(shù)組的某一個(gè)元素賦值:數(shù)組名[下標(biāo)] =值(下標(biāo)從0開(kāi)始)
數(shù)組的引用:數(shù)組名[下標(biāo)]
- 初始化數(shù)組時(shí),可以只給部分?jǐn)?shù)組元素賦值
- 對(duì)全部元素?cái)?shù)組賦值時(shí),可以不指定數(shù)組長(zhǎng)度,編譯系統(tǒng)會(huì)根據(jù)初值個(gè)數(shù)確定數(shù)組的長(zhǎng)度。
- static型數(shù)組元素不賦初值,系統(tǒng)會(huì)自動(dòng)默認(rèn)為0。
int arr1[4] = {1,2,3,4};
int arr2[4] = { 1,2 };
int arr[4] = {0];//所有元素為0
static int arr3[3];
int arr4[4];
cout << "arr1:"<<arr1[0] << arr1[1] << arr1[2] << arr1[3] << endl;
cout << "arr2:" << arr2[0] << arr2[1] << arr2[2] << arr2[3] << endl;
cout << "arr3:" << arr3[0] << arr3[1] << arr3[2] << arr3[3] << endl;
cout << "arr4:" << arr4[0] << arr4[1] << arr4[2] << arr4[3] << endl;
8.2二維數(shù)組
定義一維數(shù)組的形式:數(shù)據(jù)類(lèi)型 數(shù)據(jù)名[常量表達(dá)式1][常量表達(dá)式2]
初始化的形式:數(shù)據(jù)類(lèi)型 數(shù)組名[常量表達(dá)式1] [常量表達(dá)式2]= {初值表};
為數(shù)組的某一個(gè)元素賦值:數(shù)組名[行下標(biāo)][列下標(biāo)] =值(下標(biāo)從0開(kāi)始)
數(shù)組的引用:數(shù)組名[行下標(biāo)][列下標(biāo)]
- 將所有數(shù)據(jù)寫(xiě)在一個(gè)花括號(hào)內(nèi),自動(dòng)按照數(shù)組元素個(gè)數(shù)在內(nèi)存中排列的順序賦值
- 可對(duì)部分元素賦值,其余元素的值自動(dòng)取0.
- 定義初始化數(shù)組時(shí),可以省略第一維的長(zhǎng)度,第二維不能省,系統(tǒng)會(huì)自動(dòng)確認(rèn)行數(shù)
int arr1[2][3];
int arr[2][3] = {0];//所有元素為0
int arr2[2][3] = { {1,2,3},{4,5,6} };
int arr3[2][3] = { 1,2,3 ,4,5,6 };
int arr4[2][3] = { {1},{4,6} };
int arr5[][3] = { 1,2,3 ,4,5,6 };
字符數(shù)組
char類(lèi)型的數(shù)組,在字符數(shù)組中最后一位為’\0’)時(shí),可以看成時(shí)字符串。在C++中定義了string類(lèi),在Visual C++中定義了Cstring類(lèi)。
字符串中每一個(gè)字符占用一個(gè)字節(jié),再加上最后一個(gè)空字符。
如:
//字符串長(zhǎng)度為8個(gè)字節(jié),最后一位是'\0'。 char array[10] = "yuanrui";//yuanrui\0\0\0 //也可以不用定義字符串長(zhǎng)度,如: char arr[] = "yuanrui";//yuanrui\0
8.3 指向數(shù)組的指針
指針的概念會(huì)在后面詳細(xì)講解。
double *p; double arr[10]; p = arr;//p = &arr[0]; *(p+3);//arr[3]
8.4 數(shù)組與new(動(dòng)態(tài)創(chuàng)建數(shù)組)
一維數(shù)組:
int* arr1 = new int[2];//delete []arr1;
int* arr2 = new int[3]{ 1,2 };//delete []arr2二維數(shù)組
int m=2, n=3;
int** arr3 = new int*[2];//delete []arr3
for (int i = 0; i < 10; ++i)
{
arr3[i] = new int[3]; // delete []arr3[i]
}
int* arr4 = new int[m*n];//數(shù)據(jù)按行存儲(chǔ) delete []arr38.5 數(shù)組與函數(shù)
數(shù)組->函數(shù)
如果傳遞二維數(shù)組,形參必須制定第二維的長(zhǎng)度。
形式參數(shù)是一個(gè)指針:void function(int *param)形式參數(shù)是一個(gè)已定義大小的數(shù)組:void function(int param[10])形式參數(shù)是一個(gè)未定義大小的數(shù)組:void function(int param[])二維數(shù)組:void function(int a[][3],int size)
函數(shù)返回?cái)?shù)組
C++ 不支持在函數(shù)外返回局部變量的地址,除非定義局部變量為 static 變量。
int * function(); int** function();
8.6 獲取數(shù)組的大小
動(dòng)態(tài)創(chuàng)建(new)的基本數(shù)據(jù)類(lèi)型數(shù)組無(wú)法取得數(shù)組大小
int a[3]; //第一種方法 cout<<sizeof(a)/sizeof(a[0])<<endl; //第二種方法 cout << end(a) - begin(a) << endl; //二維數(shù)組 int arr[5][3]; int lines = sizeof(arr) / sizeof(arr[0][0]); int row = sizeof(arr) / sizeof(arr[0]);//行 int col = lines / row;//列 cout << row << "::"<<col << endl; cout << end(arr) - begin(arr) << endl;//5行
9、函數(shù)
函數(shù)是實(shí)現(xiàn)模塊化程序設(shè)計(jì)思想的重要工具, C++程序中每一項(xiàng)操作基本都是由一個(gè)函數(shù)來(lái)實(shí)現(xiàn)的,C++程序中只能有一個(gè)主函數(shù)(main)
9.1 函數(shù)聲明與定義
- 函數(shù)類(lèi)型-函數(shù)的返回值類(lèi)型;函數(shù)名-必須符合C++標(biāo)識(shí)符命名規(guī)則,后面必須跟一對(duì)括號(hào);函數(shù)體-實(shí)現(xiàn)函數(shù)功能的主題部分;參數(shù)列表-函數(shù)名后面的括號(hào)內(nèi),用于向函數(shù)傳遞數(shù)值或帶回?cái)?shù)值。
- 函數(shù)聲明中,參數(shù)名可以省略,參數(shù)類(lèi)型和函數(shù)的類(lèi)型不能省略。
- 函數(shù)聲明可以放在主調(diào)函數(shù)內(nèi)部,放在調(diào)用語(yǔ)句之前;也可以放在主調(diào)函數(shù)外,如果位于所有定義函數(shù)之前,后面函數(shù)定義順序任意,各個(gè)主調(diào)函數(shù)調(diào)用也不必再做聲明
- 當(dāng)函數(shù)定義在前,函數(shù)調(diào)用災(zāi)后,可以不用函數(shù)聲明。
后兩條總結(jié)一下就是:調(diào)用函數(shù)前,程序得知道有這個(gè)函數(shù),聲明就是提前讓程序知道有這么的玩意
函數(shù)聲明:
函數(shù)類(lèi)型 函數(shù)名(參數(shù)列表); eg: int max(int a,int b);//聲明函數(shù)時(shí),a,b可以省略 int max(int,int); void show();
函數(shù)定義:
函數(shù)類(lèi)型 函數(shù)名(參數(shù)列表)
{
函數(shù)體;
}
eg:
int max(int a,int b)
{
int z;
z = a>b?a:b;
return z;
}9.2 函數(shù)的參數(shù)與返回值
- 形參:函數(shù)定義后面括號(hào)里的參數(shù),函數(shù)調(diào)用前不占內(nèi)存。
- 實(shí)參:函數(shù)調(diào)用括號(hào)里的參數(shù),可以是常量,變量或表達(dá)式等。
形參和實(shí)參必須個(gè)數(shù)相同、類(lèi)型一致,順序一致
函數(shù)傳遞方式:傳值,指針,引用
關(guān)于指針和引用后面有詳細(xì)介紹。
//傳值-修改函數(shù)內(nèi)的形式參數(shù)對(duì)實(shí)際參數(shù)沒(méi)有影響
int add(int value)
{
value++;
return value;
}
int main()
{
int v = 10;
cout << "add() = " << add(v) << endl;//add() = 11
cout << "v = " << v << endl;//v = 10
return 0;
}
//指針-修改形式參數(shù)會(huì)影響實(shí)際參數(shù)
int add(int* pValue)
{
(*pValue)++;
return *pValue;
}
int main()
{
int v = 10;
cout << "add() = " << add(&v) << endl;//add() = 11
cout << "v = " << v << endl;//v = 11
return 0;
}
//引用-修改形式參數(shù)會(huì)影響實(shí)際參數(shù)
int add(int &value)
{
value++;
return value;
}
int main()
{
int v = 10;
cout << "add() = " << add(v) << endl;//add() = 11
cout << "v = " << v << endl;//v = 11
return 0;
}有默認(rèn)值參數(shù)的函數(shù)
int sum(int a, int b=2)
{
return (a + b);
}
int main ()
{
cout << "Total value is :" << sum(100, 200);<< endl;//Total value is :300
cout << "Total value is :" << sum(100);<< endl;//Total value is :102
return 0;
}函數(shù)的返回值
- 返回值通過(guò)return給出,return后面跟表達(dá)式,且只能放回一個(gè)值;如果沒(méi)有表達(dá)式,可以不寫(xiě)return;return后面的括號(hào)可有可無(wú)。
- return語(yǔ)句中的表達(dá)式類(lèi)型應(yīng)與函數(shù)類(lèi)型一致,否則自動(dòng)轉(zhuǎn)換類(lèi)型(函數(shù)類(lèi)型決定返回值類(lèi)型)
9.3 函數(shù)調(diào)用
函數(shù)可以單獨(dú)作為一個(gè)語(yǔ)句使用。有返回值的函數(shù),可將函數(shù)調(diào)用作為語(yǔ)句的一部分,利用返回值參與運(yùn)算。
函數(shù)調(diào)用形式:參數(shù)傳遞–>函數(shù)體執(zhí)行–>返回主調(diào)函數(shù)
函數(shù)名(實(shí)參列表); show();
函數(shù)的嵌套調(diào)用:
int a()
{
return 666;
}
int b(int sum)
{
return sum+a()
}
int main()
{
cout<<b(222)<<endl;//888
return 0;
}函數(shù)的遞歸調(diào)用:直接遞歸調(diào)用和間接遞歸調(diào)用
- 一個(gè)函數(shù)直接或間接遞歸調(diào)用該函數(shù)本身,稱(chēng)為函數(shù)的遞歸調(diào)用
- 遞歸和回歸:原問(wèn)題=>子問(wèn)題 子問(wèn)題的解=>原問(wèn)題的解
//直接遞歸調(diào)用:求1+...n的值
int total(int sum)
{
if (sum == 1)
{
return 1;
}
return sum + total(sum - 1);
}
int main()
{
cout << "total = " << total(10) << endl;//total = 55
system("pause");
return 0;
}
//間接遞歸調(diào)用
int f2();
int f1()
{
...
f2()
}
int f2()
{
f1();
}9.4 函數(shù)重載
同一個(gè)函數(shù)名對(duì)應(yīng)不同的函數(shù)實(shí)現(xiàn),每一類(lèi)實(shí)現(xiàn)對(duì)應(yīng)著一個(gè)函數(shù)體,名字相同,功能相同,只是參數(shù)的類(lèi)型或參數(shù)的個(gè)數(shù)不同。
多個(gè)同名函數(shù)只是函數(shù)類(lèi)型(函數(shù)返回值類(lèi)型)不同時(shí),它們不是重載函數(shù)
int add(int a,int b)
{
return a+b;
}
double add(double a,double b)
{
return a+b;
}
int add(int a,int b,int c)
{
return a+b+c;
}9.5 內(nèi)聯(lián)(inline)函數(shù)
c++在編譯時(shí)可以講調(diào)用的函數(shù)代碼嵌入到主調(diào)函數(shù)中,這種嵌入到主調(diào)函數(shù)中的函數(shù)稱(chēng)為內(nèi)聯(lián)函數(shù),又稱(chēng)為內(nèi)嵌函數(shù)或內(nèi)置函數(shù)。
- 定義內(nèi)聯(lián)函數(shù)時(shí),在函數(shù)定義和函數(shù)原型聲明時(shí)都使用inline,也可以只在其中一處使用,其效果一樣。
- 內(nèi)聯(lián)函數(shù)在編譯時(shí)用內(nèi)聯(lián)函數(shù)函數(shù)的函數(shù)體替換,所以不發(fā)生函數(shù)調(diào)用,不需要保護(hù)現(xiàn)場(chǎng),恢復(fù)現(xiàn)場(chǎng),節(jié)省了開(kāi)銷(xiāo)。
- 內(nèi)聯(lián)函數(shù)增加了目標(biāo)程序的代碼量。因此,一般只將函數(shù)規(guī)模很小且使用頻繁的函數(shù)聲明為內(nèi)聯(lián)函數(shù)。
- 當(dāng)內(nèi)聯(lián)函數(shù)中實(shí)現(xiàn)過(guò)于復(fù)雜時(shí),編譯器會(huì)將它作為一個(gè)普通函數(shù)處理,所以?xún)?nèi)聯(lián)函數(shù)內(nèi)不能包含循環(huán)語(yǔ)句和switch語(yǔ)句。
內(nèi)聯(lián)函數(shù)格式如下:
inline 函數(shù)類(lèi)型 函數(shù)名(形參列表)
{
函數(shù)體;
}
inline int add(int a, int b)
{
return a + b;
}9.6 洞悉內(nèi)聯(lián)函數(shù)底層原理
1.使用Visual Studio 2015創(chuàng)建一個(gè)C++Win32控制臺(tái)程序,點(diǎn)擊項(xiàng)目->項(xiàng)目屬性設(shè)置內(nèi)聯(lián)函數(shù)優(yōu)化

2.編寫(xiě)內(nèi)聯(lián)函數(shù)代碼,設(shè)置斷點(diǎn),debug啟動(dòng)
#include <iostream>
#include <string>
using namespace std;
inline int add(int a, int b)
{
return a + b;//斷點(diǎn)1
}
int main()
{
int result = add(12, 34);
cout << result << endl;//斷點(diǎn)2
return 0;
}3.調(diào)試->窗口->反匯編,然后就能看到編譯后的匯編程序
... int result = add(12, 34); 00B620DE mov eax,0Ch 00B620E3 add eax,22h //對(duì)eax中和22h中值進(jìn)行相加,賦值給eax 00B620E6 mov dword ptr [result],eax cout << result << endl; 00B620E9 mov esi,esp 00B620EB push offset std::endl<char,std::char_traits<char> > (0B610A5h) 00B620F0 mov edi,esp 00B620F2 mov eax,dword ptr [result] 00B620F5 push eax 00B620F6 mov ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0B6D098h)] 00B620FC call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B6D0A8h)] 00B62102 cmp edi,esp 00B62104 call __RTC_CheckEsp (0B611C7h) 00B62109 mov ecx,eax 00B6210B call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B6D0ACh)] 00B62111 cmp esi,esp 00B62113 call __RTC_CheckEsp (0B611C7h) return 0;
4.從匯編代碼中可以代碼編譯后內(nèi)聯(lián)函數(shù)直接嵌入到主函數(shù)中,并且斷點(diǎn)1不會(huì)執(zhí)行到,下面是沒(méi)使用內(nèi)聯(lián)函數(shù)(去掉inline關(guān)鍵字)的匯編代碼:
int result = add(12, 34);
00291A4E push 22h
00291A50 push 0Ch
00291A52 call add (02914D8h) //調(diào)用add函數(shù)
00291A57 add esp,8//移動(dòng)堆棧指針esp,繼續(xù)執(zhí)行主函數(shù)
00291A5A mov dword ptr [result],eax
cout << result << endl;
00291A5D mov esi,esp
00291A5F push offset std::endl<char,std::char_traits<char> > (02910A5h)
00291A64 mov edi,esp
00291A66 mov eax,dword ptr [result]
00291A69 push eax
00291A6A mov ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (029D098h)]
cout << result << endl;
00291A70 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (029D0A8h)]
00291A76 cmp edi,esp
00291A78 call __RTC_CheckEsp (02911C7h)
00291A7D mov ecx,eax
00291A7F call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (029D0ACh)]
00291A85 cmp esi,esp
00291A87 call __RTC_CheckEsp (02911C7h)
system("pause");
00291A8C mov esi,esp
00291A8E push offset string "pause" (0299B30h)
00291A93 call dword ptr [__imp__system (029D1DCh)]
00291A99 add esp,4
00291A9C cmp esi,esp
00291A9E call __RTC_CheckEsp (02911C7h)
return 0;從以上代碼代碼可以看出,在主函數(shù)中調(diào)用(call)了add函數(shù)。
5.在內(nèi)聯(lián)函數(shù)中添加幾個(gè)循環(huán)后,編譯器就把內(nèi)聯(lián)函數(shù)當(dāng)做普通函數(shù)看待了,代碼如下:
inline int add(int a, int b)
{
int sum = 0;
for (int i = 0; i < 100; i++)
a++;
for (int i = 0; i < 100; i++)
{
for (int i = 0; i < 100; i++)
{
sum++;
}
}
return a + b;
}
int main()
{
int result = add(12, 34);
cout << result << endl;
return 0;
}int result = add(12, 34); 00181A4E push 22h 00181A50 push 0Ch 00181A52 call add (01814ECh) /// 00181A57 add esp,8 00181A5A mov dword ptr [result],eax cout << result << endl; 00181A5D mov esi,esp 00181A5F push offset std::endl<char,std::char_traits<char> > (01810A5h) 00181A64 mov edi,esp 00181A66 mov eax,dword ptr [result] 00181A69 push eax 00181A6A mov ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (018D098h)] cout << result << endl; 00181A70 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (018D0A8h)] 00181A76 cmp edi,esp 00181A78 call __RTC_CheckEsp (01811C7h) 00181A7D mov ecx,eax 00181A7F call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (018D0ACh)] 00181A85 cmp esi,esp 00181A87 call __RTC_CheckEsp (01811C7h) return 0; 00181AA3 xor eax,eax
10、字符串(string)
10.1 C風(fēng)格的字符串(字符數(shù)組)
C風(fēng)格的字符串實(shí)際上是使用 null 字符 ‘\0’ 終止的一維字符數(shù)組。
輸入字符串長(zhǎng)度一定小于已定義的字符數(shù)組長(zhǎng)度,最后一位是/0終止符號(hào);不然輸出時(shí)無(wú)法知道在哪里結(jié)束。
字符數(shù)組的定義和初始化
char a[5]
//字符個(gè)數(shù)不夠,補(bǔ)0; 字符個(gè)數(shù)超過(guò)報(bào)錯(cuò)
char str[7] = {'h','e','i','r','e','n'};
char str[] = {'h','e','i','r','e','n'};
cin>>str;//輸入 輸入字符串長(zhǎng)度一定小于已定義的字符數(shù)組長(zhǎng)度
cout<<str;//輸出字符串的處理函數(shù)
strcat(char s1[],const char s2[]);//將s2接到s1上 strcpy(char s1[],const char s2[]);//將s2復(fù)制到s1上 strcmp(const char s1[],const char s2[]);//比較s1,s2 s1>s2返回1 相等返回1,否則返回-1 strlen(char s[]);//計(jì)算字符串s的長(zhǎng)度 字符串s的實(shí)際長(zhǎng)度,不包括\0在內(nèi)
10.2 C++中的字符串(string)
字符串的定義和初始化
//定義 string 變量; string str1; //賦值 string str2 = "ShangHai"; string str3 = str2; str3[3] = '2';//對(duì)某個(gè)字符賦值 //字符串?dāng)?shù)組 string 數(shù)組名[常量表達(dá)式] string arr[3];
字符串的處理函數(shù)
#include <iostream>
#include <algorithm>
#include <string>
string str;//生成空字符串
string s(str);//生成字符串為str的復(fù)制品
string s(str, strbegin,strlen);//將字符串str中從下標(biāo)strbegin開(kāi)始、長(zhǎng)度為strlen的部分作為字符串初值
string s(cstr, char_len);//以C_string類(lèi)型cstr的前char_len個(gè)字符串作為字符串s的初值
string s(num ,c);//生成num個(gè)c字符的字符串
string s(str, stridx);//將字符串str中從下標(biāo)stridx開(kāi)始到字符串結(jié)束的位置作為字符串初值
size()和length();//返回string對(duì)象的字符個(gè)數(shù)
max_size();//返回string對(duì)象最多包含的字符數(shù),超出會(huì)拋出length_error異常
capacity();//重新分配內(nèi)存之前,string對(duì)象能包含的最大字符數(shù)
>,>=,<,<=,==,!=//支持string與C-string的比較(如 str<”hello”)。 使用>,>=,<,<=這些操作符的時(shí)候是根據(jù)“當(dāng)前字符特性”將字符按字典順序進(jìn)行逐一得 比較,string (“aaaa”) <string(aaaaa)。
compare();//支持多參數(shù)處理,支持用索引值和長(zhǎng)度定位子串來(lái)進(jìn)行比較。返回一個(gè)整數(shù)來(lái)表示比較結(jié)果,返回值意義如下:0:相等 1:大于 -1:
push_back()
insert( size_type index, size_type count, CharT ch );//在index位置插入count個(gè)字符ch
insert( size_type index, const CharT* s );//index位置插入一個(gè)常量字符串
insert( size_type index, const CharT* s, size_type n);//index位置插入常量字符串
insert( size_type index, const basic_string& str );//index位置插入常量string中的n個(gè)字符
insert( size_type index, const basic_string& str, size_type index_str, size_type n);//index位置插入常量str的從index_str開(kāi)始的n個(gè)字符
insert( size_type index, const basic_string& str,size_type index_str, size_type count = npos);//index位置插入常量str從index_str開(kāi)始的count個(gè)字符,count可以表示的最大值為npos.這個(gè)函數(shù)不構(gòu)成重載 npos表示一個(gè)常數(shù),表示size_t的最大值,string的find函數(shù)如果未找到指定字符,返回的就是一個(gè)npos
iterator insert( iterator pos, CharT ch );
iterator insert( const_iterator pos, CharT ch );
void insert( iterator pos, size_type n, CharT ch );//迭代器指向的pos位置插入n個(gè)字符ch
iterator insert( const_iterator pos, size_type count, CharT ch );//迭代器指向的pos位置插入count個(gè)字符ch
void insert( iterator pos, InputIt first, InputIt last );
iterator insert( const_iterator pos, InputIt first, InputIt last );
append() 和 + 操作符
//訪問(wèn)string每個(gè)字符串
string s1("yuanrui"); // 調(diào)用一次構(gòu)造函數(shù)
// 方法一: 下標(biāo)法
for( int i = 0; i < s1.size() ; i++ )
cout<<s1[i];
// 方法二:正向迭代器
for( string::iterator iter = s1.begin();; iter < s1.end() ; iter++)
cout<<*iter;
// 方法三:反向迭代器
for(string::reverse_iterator riter = s1.rbegin(); ; riter < s1.rend() ; riter++)
cout<<*riter;
iterator erase(iterator p);//刪除字符串中p所指的字符
iterator erase(iterator first, iterator last);//刪除字符串中迭代器區(qū)間[first,last)上所有字符
string& erase(size_t pos = 0, size_t len = npos);//刪除字符串中從索引位置pos開(kāi)始的len個(gè)字符
void clear();//刪除字符串中所有字符
string& replace(size_t pos, size_t n, const char *s);//將當(dāng)前字符串從pos索引開(kāi)始的n個(gè)字符,替換成字符串s
string& replace(size_t pos, size_t n, size_t n1, char c); //將當(dāng)前字符串從pos索引開(kāi)始的n個(gè)字符,替換成n1個(gè)字符c
string& replace(iterator i1, iterator i2, const char* s);//將當(dāng)前字符串[i1,i2)區(qū)間中的字符串替換為字符串s
//tolower()和toupper()函數(shù) 或者 STL中的transform算法
string s = "ABCDEFG";
for( int i = 0; i < s.size(); i++ )
s[i] = tolower(s[i]);
transform(s.begin(),s.end(),s.begin(),::tolower);
size_t find (constchar* s, size_t pos = 0) const;//在當(dāng)前字符串的pos索引位置開(kāi)始,查找子串s,返回找到的位置索引,-1表示查找不到子串
size_t find (charc, size_t pos = 0) const;//在當(dāng)前字符串的pos索引位置開(kāi)始,查找字符c,返回找到的位置索引,-1表示查找不到字符
size_t rfind (constchar* s, size_t pos = npos) const;//在當(dāng)前字符串的pos索引位置開(kāi)始,反向查找子串s,返回找到的位置索引,-1表示查找不到子串
size_t rfind (charc, size_t pos = npos) const;//在當(dāng)前字符串的pos索引位置開(kāi)始,反向查找字符c,返回找到的位置索引,-1表示查找不到字符
size_tfind_first_of (const char* s, size_t pos = 0) const;//在當(dāng)前字符串的pos索引位置開(kāi)始,查找子串s的字符,返回找到的位置索引,-1表示查找不到字符
size_tfind_first_not_of (const char* s, size_t pos = 0) const;//在當(dāng)前字符串的pos索引位置開(kāi)始,查找第一個(gè)不位于子串s的字符,返回找到的位置索引,-1表示查找不到字符
size_t find_last_of(const char* s, size_t pos = npos) const;//在當(dāng)前字符串的pos索引位置開(kāi)始,查找最后一個(gè)位于子串s的字符,返回找到的位置索引,-1表示查找不到字符
size_tfind_last_not_of (const char* s, size_t pos = npos) const;//在當(dāng)前字符串的pos索引位置開(kāi)始,查找最后一個(gè)不位于子串s的字符,返回找到的位置索引,-1表示查找不到子串
sort(s.begin(),s.end());
substr(pos,n);//返回字符串從下標(biāo)pos開(kāi)始n個(gè)字符
strtok()
char str[] = "I,am,a,student; hello world!";
const char *split = ",; !";
char *p2 = strtok(str,split);
while( p2 != NULL )
{
cout<<p2<<endl;
p2 = strtok(NULL,split);
}11、指針和引用
11.1 指針
指針是一個(gè)變量,其值為另一個(gè)變量的地址。即內(nèi)存位置的直接地址。
聲明的一般形式:
數(shù)據(jù)類(lèi)型是指針變量所指向的變量的數(shù)據(jù)類(lèi)型,*表示其后的變量為指針變量
數(shù)據(jù)類(lèi)型 *指針變量名; int *ip; //整型的指針 double *dp; //double 型的指針 float *fp; //浮點(diǎn)型的指針 char *ch; //字符型的指針
指針變量的初始化:
- &是取地址運(yùn)算符,&變量名表示變量的地址。
- 變量的數(shù)據(jù)類(lèi)型必須于指針變量的數(shù)據(jù)類(lèi)型一致。
- 為了安全起見(jiàn),有時(shí)會(huì)把指針初始化為空指針(NULL或0)
數(shù)據(jù)類(lèi)型 *指針變量名 = &變量名; *指針變量名 = &變量名; int a; int *p = &a; int *p2; p2 = &a;
指針變量的引用:
- & 取地址符 * 指針運(yùn)算符(間接運(yùn)算符),其后是指針變量,表示該指針變量所指向的變量。
- & *的優(yōu)先級(jí)是相同的,結(jié)合方式都是自左向右。比如 &*p等價(jià)于&(*p)。
int x = 3; int y; int *p; p = &x; y = *p;//y = a
指針運(yùn)算(地址運(yùn)算)
- 算術(shù)運(yùn)算(移動(dòng)指針運(yùn)算):加減,自增自減。
- p+n運(yùn)算得到的地址是p+n*sizeof(數(shù)據(jù)類(lèi)型)。
- 兩個(gè)相同數(shù)據(jù)類(lèi)型的指針可以進(jìn)行加減運(yùn)算,一般用于數(shù)組的操作中。
- 關(guān)系運(yùn)算:指針指向同一串連續(xù)存儲(chǔ)單元才有意義,比如數(shù)組。與0比較,判斷是不是空指針。
- 賦值運(yùn)算:變量地址賦值給指針變量,數(shù)組元素地址賦值給指針變量,指針變量賦值給其他指針變量。
int arr[10],len; int *p1 = &arr[2],*p2 = &arr[5]; len = p2-p1;//arr[2] 和arr[5]之間的元素個(gè)數(shù) 3
new和delete運(yùn)算符
- new-為變量分配內(nèi)存空間;
- 可以通過(guò)判斷new返回的指針的值,判斷空間是否分配成功。
- delete-釋放空間
指針變量 = new 數(shù)據(jù)類(lèi)型(初值);
delete 指針變量;
delete[] 指針變量;//釋放為多個(gè)變量分配的地址
int *ip;
ip= new int(1);
delete ip;
int *ip;
ip= new int[10];
for (int i = 0; i < 10;i++)
{
ip[i] = i;
}
delete[] ip;
int a[3][4] = {0};指針與數(shù)組
- 數(shù)組名是數(shù)組的首地址,eg:arr為arr[0]的地址。
- 訪問(wèn)數(shù)組元素:arr[i],(arr+i),(p+i),p[i]
- 二維數(shù)組:arr+i == &arr[i],arr[i] == &arr[i][0] ,*(arr[i]+j) == arr[i][j]
- 指針訪問(wèn)二維數(shù)組:指向二維數(shù)組元素,指向一維數(shù)組
- 數(shù)組指針:
數(shù)據(jù)類(lèi)型 (*指針變量名) [m]
int arr[10];
int *p1 = arr;// *p1 = &arr[0];
int a[3][5] = { 0 };
int(*ap)[5];
ap = a;
ap+1;//表示下一個(gè)一維數(shù)組指針與字符串
- 字符串?dāng)?shù)組名:
char ch[] = "heiren";char *p = ch; - 字符串:
char *p = "heiren"; - 指針賦值運(yùn)算:
char * p;p = "Heiren";指針與函數(shù),指針可以作為函數(shù)的參數(shù),也可以作為函數(shù)的返回值。
11.2 引用
引用可以看做是數(shù)據(jù)的一個(gè)別名,通過(guò)這個(gè)別名和原來(lái)的名字都能夠找到這份數(shù)據(jù),類(lèi)似于window中的快捷方式。
- 引用不占內(nèi)存空間,必須在定義的同時(shí)初始化,且不能再引用其他數(shù)據(jù)。
- 引用在定義時(shí)需要添加&,在使用時(shí)不能添加&,使用時(shí)添加&表示取地址
引用型變量聲明:數(shù)據(jù)類(lèi)型 &引用名 = 變量名;
int a; int &b = a;//a和b表示相同的變量,具有相同的地址。
引用可以作為函數(shù)參數(shù),也可以作為函數(shù)返回值。
void swap(int &r1, int &r2) {
int temp = r1;
r1 = r2;
r2 = temp;
}
int &add1(int &r) {
r += 1;
return r;
}
int main()
{
int a = 12;
int b = add1(a);
cout << a << " "<<b << endl;//13 13
return 0;
}將引用作為函數(shù)返回值時(shí)不能返回局部數(shù)據(jù)的引用,因?yàn)楫?dāng)函數(shù)調(diào)用完成后局部數(shù)據(jù)就會(huì)被銷(xiāo)毀。
函數(shù)在棧上運(yùn)行,函數(shù)掉用完,后面的函數(shù)調(diào)用會(huì)覆蓋之前函數(shù)的局部數(shù)據(jù)。
int &add1(int &r) {
r += 1;
int res = r;
return res;
}
void test()
{
int xx = 123;
int yy = 66;
}
int main()
{
int a = 12;
int &b = add1(a);
int &c = add1(a);
test();//函數(shù)調(diào)用,覆蓋之前函數(shù)的局部數(shù)據(jù)
cout << a << " "<<b <<" "<< c<<endl;//14 -858993460 -858993460
return 0;
}12、自定義數(shù)據(jù)類(lèi)型
12.1 結(jié)構(gòu)體
結(jié)構(gòu)體可以包含不同數(shù)據(jù)類(lèi)型的結(jié)構(gòu)。
定義結(jié)構(gòu)體的一般形式
struct 結(jié)構(gòu)體類(lèi)型名
{
成員類(lèi)型1 成員名1;
成員類(lèi)型2 成員名2;
... ...
成員類(lèi)型n 成員名n;
};結(jié)構(gòu)體變量名的定義和初始化:
//定義結(jié)構(gòu)體同時(shí)聲明結(jié)構(gòu)體變量名
struct 結(jié)構(gòu)體類(lèi)型名
{
成員類(lèi)型1 成員名1;
成員類(lèi)型2 成員名2;
... ...
成員類(lèi)型n 成員名n;
}變量名1,變量名2,...變量名n;
//先定義結(jié)構(gòu)體
[struct] 結(jié)構(gòu)體類(lèi)型名 變量名;
//直接定義
struct
{
成員類(lèi)型1 成員名1;
成員類(lèi)型2 成員名2;
... ...
成員類(lèi)型n 成員名n;
}變量名1,變量名2,...變量名n;
struct person
{
int year;
int age;
string name;
}p1 = {2019,24,"heiren"}, p1 = { 2020,24,"heiren" };
struct person
{
int year;
int age;
string name;
};
struct person p1 = { 2019,24,"heiren" }, p1 = { 2020,24,"heiren" };
struct
{
int year;
int age;
string name;
}p1 = {2019,24,"heiren"}, p1 = { 2020,24,"heiren" };結(jié)構(gòu)體變量的使用:
- 具有相同類(lèi)型的結(jié)構(gòu)體變量可以進(jìn)行賦值運(yùn)算,但是不能輸入輸出
- 對(duì)結(jié)構(gòu)體變量的成員引用:
結(jié)構(gòu)體變量名.成員名 - 指向結(jié)構(gòu)體的指針變量引用格式:
指針變量名->成員名;結(jié)構(gòu)體數(shù)組的定義,初始化和使用與結(jié)構(gòu)體變量、基本類(lèi)型數(shù)組相似
struct person
{
int year;
int age;
string name;
}p[2] ={ {2019,24,"heiren"}, { 2020,24,"heiren" }};//可以不指定數(shù)組元素個(gè)數(shù)
p[1].age;結(jié)構(gòu)體作為函數(shù)傳遞有三種:值傳遞,引用傳遞,指針傳遞
12.2 結(jié)構(gòu)體大小和字節(jié)對(duì)齊
現(xiàn)代計(jì)算機(jī)中內(nèi)存空間都是按照byte劃分的,從理論上講似乎對(duì)任何類(lèi)型的變量的訪問(wèn)可以從任何地址開(kāi)始,但實(shí)際情況是在訪問(wèn)特定類(lèi)型變量的時(shí)候經(jīng)常在特 定的內(nèi)存地址訪問(wèn),這就需要各種類(lèi)型數(shù)據(jù)按照一定的規(guī)則在空間上排列,而不是順序的一個(gè)接一個(gè)的排放,這就是對(duì)齊.
為什么需要字節(jié)對(duì)齊?各個(gè)硬件平臺(tái)對(duì)存儲(chǔ)空間的處理上有很大的不同。一些平臺(tái)對(duì)某些特定類(lèi)型的數(shù)據(jù)只能從某些特定地址開(kāi)始存取。比如有些平臺(tái)每次讀都是從偶地址開(kāi)始,如果一個(gè)int型(假設(shè)為32位系統(tǒng))如果存放在偶地址開(kāi)始的地方,那么一個(gè)讀周期就可以讀出這32bit,而如果存放在奇地址開(kāi)始的地方,就需要2個(gè)讀周期,并對(duì)兩次讀出的結(jié)果的高低字節(jié)進(jìn)行拼湊才能得到該32bit數(shù)據(jù)。
三個(gè)個(gè)概念:
- 自身對(duì)齊值:數(shù)據(jù)類(lèi)型本身的對(duì)齊值,結(jié)構(gòu)體或類(lèi)的的自身對(duì)齊值是其成員中最大的那個(gè)值,例如char類(lèi)型的自身對(duì)齊值是1,short類(lèi)型是2;
- 指定對(duì)齊值:編譯器或程序員指定的對(duì)齊值,32位單片機(jī)的指定對(duì)齊值默認(rèn)是4;
- 有效對(duì)齊值:自身對(duì)齊值和指定對(duì)齊值中較小的那個(gè)。
字節(jié)對(duì)齊的三個(gè)準(zhǔn)則 - 結(jié)構(gòu)體變量的首地址能夠被其有效對(duì)齊值的大小所整除
- 結(jié)構(gòu)體的總大小為結(jié)構(gòu)體有效對(duì)齊值的整數(shù)倍。
- 結(jié)構(gòu)體每個(gè)成員相對(duì)于結(jié)構(gòu)體首地址的偏移量都是有效對(duì)齊值的整數(shù)倍。
可以通過(guò)#pragma pack(n)來(lái)設(shè)定變量以n字節(jié)對(duì)齊方式
舉個(gè)例子
//指定對(duì)齊值=8
struct st
{
// 空結(jié)構(gòu)體大小1
char c1;//1
char c2;//2
int i1;//8 int 起始地址按照字節(jié)對(duì)齊的原理應(yīng)該是它長(zhǎng)度4的整數(shù)倍
char c3;//12
short s4;//12 short 起始地址按照字節(jié)對(duì)齊的原理應(yīng)該是它長(zhǎng)度2的整數(shù)倍 12 + 2 = 12
double d;//24 double 起始地址按照字節(jié)對(duì)齊的原理應(yīng)該是它長(zhǎng)度8的整數(shù)倍 12->16 + 8 = 24
char c4;//32 24 + 4 = 28 結(jié)構(gòu)體的總大小為8的整數(shù)倍 28->32
int i2;//32 28+4 = 32
int i3;//40
short s5;//40
};
cout << sizeof(st) << endl;//40
//指定對(duì)齊值=4
#pragma pack(4)
struct st
{
//1 空結(jié)構(gòu)體大小1
char c1;//1
char c2;//2
int i1;//8
char c3;//12
short s4;//12
double d;//20
char c4;//24
int i2;//28
int i3;//32
short s5;//36
}s;
cout << sizeof(st) << endl;//36
12.3 公用體(union)
幾個(gè)不同的變量共享同一個(gè)地址開(kāi)始的內(nèi)存空間。
- 成員類(lèi)型可以是基本數(shù)據(jù)類(lèi)型,也可以是構(gòu)造數(shù)據(jù)類(lèi)型。
- 公用體變量初始化時(shí),只能對(duì)第一個(gè)成員賦值。
- 公用體變量所占的內(nèi)存長(zhǎng)度等于最長(zhǎng)的成員長(zhǎng)度。
- 公用體變量在一個(gè)時(shí)刻只能一個(gè)成員發(fā)揮作用,賦值時(shí),成員之間會(huì)互相覆蓋,最后一次被賦值的成員起作用。
定義
union 共同體類(lèi)型名
{
成員類(lèi)型1 成員名1;
成員類(lèi)型2 成員名2;
... ...
成員類(lèi)型n 成員名n;
};初始化
union data
{
int i;
float f;
char c;
}x = {123};
union data
{
float f;
int i;
char c;
};
data x = {12.3};
union
{
char c;
int i;
float f;
}x = {'y‘};引用
共同體變量名.成員名;
union data
{
int i;
float f;
char c;
}x = {12};
int main()
{
cout << x.i << " " << x.f << " " << x.c << endl;//12 1.68156e-44
x.c = 'c';
cout << x.i <<" "<< x.f << " " << x.c << endl;//99 1.38729e-43 c
return 0;
}12.4 枚舉(enum)和typedef聲明
枚舉已經(jīng)在前面的章節(jié)介紹過(guò),這里就不在贅述了。
typedef-為已存在的數(shù)據(jù)類(lèi)型定義一個(gè)新的類(lèi)型名稱(chēng),不能定義變量。
typedef聲明格式:typedef 類(lèi)型名稱(chēng) 類(lèi)型標(biāo)識(shí)符;
typedef char *CP; typedef int INTEGER;
13、面向?qū)ο?/h2>
13.1 類(lèi)
類(lèi)也是一種數(shù)據(jù)類(lèi)型。
類(lèi)的聲明:
class 類(lèi)名
{
public:
公有數(shù)據(jù)成員;
公有成員函數(shù);
private:
私有數(shù)據(jù)成員;
私有成員函數(shù);
protected:
保護(hù)數(shù)據(jù)成員;
保護(hù)成員函數(shù);
};成員函數(shù)的定義:類(lèi)內(nèi),類(lèi)外,類(lèi)外內(nèi)聯(lián)函數(shù)
//類(lèi)外
返回類(lèi)型 類(lèi)名:成員函數(shù)名(參數(shù)列表)
{
函數(shù)體;
}
//內(nèi)聯(lián)函數(shù):類(lèi)外
inline 返回類(lèi)型 類(lèi)名:成員函數(shù)名(參數(shù)列表)
{
函數(shù)體;
}內(nèi)聯(lián)函數(shù)的代碼會(huì)直接嵌入到主調(diào)函數(shù)中,可以節(jié)省調(diào)用時(shí)間,如果成員函數(shù)在類(lèi)內(nèi)定義,自動(dòng)為內(nèi)聯(lián)函數(shù)。
13.2 類(lèi)成員的訪問(wèn)權(quán)限以及類(lèi)的封裝
- 和Java、C#不同的是,C++中public、private、protected只能修飾類(lèi)的成員,不能修飾類(lèi),C++中的類(lèi)沒(méi)有共有私有之分
- 類(lèi)內(nèi)部沒(méi)有訪問(wèn)權(quán)限的限制,都可以互相訪問(wèn)。
- 在C++中用class定義的類(lèi)中,其成員的默認(rèn)存取權(quán)限是private。
| 類(lèi)外 | 派生類(lèi) | 類(lèi)內(nèi) | |
|---|---|---|---|
| public | Y | Y | Y |
| protected | N | Y | Y |
| private | N | N | Y |
13.3 對(duì)象
//1.聲明類(lèi)同時(shí)定義對(duì)象
class 類(lèi)名
{
類(lèi)體;
}對(duì)象名列表;
//2.先聲明類(lèi),再定義對(duì)象
類(lèi)名 對(duì)象名(參數(shù)列表);//參數(shù)列表為空時(shí),()可以不寫(xiě)
//3. 不出現(xiàn)類(lèi)名,直接定義對(duì)象
class
{
類(lèi)體;
}對(duì)象名列表;
//4.在堆上創(chuàng)建對(duì)象
Person p(123, "yar");//在棧上創(chuàng)建對(duì)象
Person *pp = new Person(234,"yar");//在堆上創(chuàng)建對(duì)象注:不可以在定義類(lèi)的同時(shí)對(duì)其數(shù)據(jù)成員進(jìn)行初始化,因?yàn)轭?lèi)不是一個(gè)實(shí)體,不合法但是能編譯運(yùn)行
對(duì)象成員的引用:對(duì)象名.數(shù)據(jù)成員名 或者 對(duì)象名.成員函數(shù)名(參數(shù)列表)
13.4 構(gòu)造函數(shù)
是一種特殊的成員函數(shù),主要功能是為對(duì)象分配存儲(chǔ)空間,以及為類(lèi)成員變量賦初值
- 構(gòu)造函數(shù)名必須與類(lèi)名相同
- 沒(méi)有任何返回值和返回類(lèi)型
- 創(chuàng)建對(duì)象自動(dòng)調(diào)用,不需要用戶(hù)來(lái)調(diào)用,且只掉用一次
- 類(lèi)沒(méi)有定義任何構(gòu)造函數(shù),編譯系統(tǒng)會(huì)自動(dòng)為這個(gè)類(lèi)生成一個(gè)默認(rèn)的無(wú)參構(gòu)造函數(shù)
構(gòu)造函數(shù)定義
//1.類(lèi)中定義 2.類(lèi)中聲明,類(lèi)外定義
[類(lèi)名::]構(gòu)造函數(shù)名(參數(shù)列表)
{
函數(shù)體
}創(chuàng)建對(duì)象
類(lèi)名 對(duì)象名(參數(shù)列表);//參數(shù)列表為空時(shí),()可以不寫(xiě)
帶默認(rèn)參數(shù)的構(gòu)造函數(shù)
class Person
{
public:
Person(int = 0,string = "張三");
void show();
private:
int age;
string name;
};
Person::Person(int a, string s)
{
cout<<a<<" "<<s<<endl;
age = a;
name = s;
}
void Person::show()
{
cout << "age="<<age << endl;
cout << "name=" <<name << endl;
}
int main()
{
Person p; //0 張三
Person p2(12);//12 張三
Person p3(123, "yar");//123 yar
return 0;
}帶參數(shù)初始化表的構(gòu)造函數(shù)
類(lèi)名::構(gòu)造函數(shù)名(參數(shù)列表):參數(shù)初始化表
{
函數(shù)體;
}
參數(shù)初始化列表的一般形式:
參數(shù)名1(初值1),參數(shù)名2(初值2),...,參數(shù)名n(初值n)
class Person
{
public:
Person(int = 0,string = "張三");
void show();
private:
int age;
string name;
};
Person::Person(int a, string s):age(a),name(s)
{
cout << a << " " << s << endl;
}構(gòu)造函數(shù)重載:構(gòu)造函數(shù)名字相同,參數(shù)個(gè)數(shù)和參數(shù)類(lèi)型不一樣。
class Person
{
public:
Person();
Person(int = 0,string = "張三");
Person(double,string);
void show();
private:
int age;
double height;
string name;
};
...拷貝構(gòu)造函數(shù)
類(lèi)名::類(lèi)名(類(lèi)名&對(duì)象名)
{
函數(shù)體;
}
class Person
{
public:
Person(Person &p);//聲明拷貝構(gòu)造函數(shù)
Person(int = 0,string = "張三");
void show();
private:
int age;
string name;
};
Person::Person(Person &p)//定義拷貝構(gòu)造函數(shù)
{
cout << "拷貝構(gòu)造函數(shù)" << endl;
age = 0;
name = "ABC";
}
Person::Person(int a, string s):age(a),name(s)
{
cout << a << " " << s << endl;
}
int main()
{
Person p(123, "yar");
Person p2(p);
p2.show();
return 0;
}
//輸出
123 yar
拷貝構(gòu)造函數(shù)
age=0
name=ABC13.5 析構(gòu)函數(shù)
是一種特殊的成員函數(shù),當(dāng)對(duì)象的生命周期結(jié)束時(shí),用來(lái)釋放分配給對(duì)象的內(nèi)存空間愛(ài)你,并做一些清理的工作。
- 析構(gòu)函數(shù)名與類(lèi)名必須相同。
- 析構(gòu)函數(shù)名前面必須加一個(gè)波浪號(hào)~。
- 沒(méi)有參數(shù),沒(méi)有返回值,不能重載。
- 一個(gè)類(lèi)中只能有一個(gè)析構(gòu)函數(shù)。
- 沒(méi)有定義析構(gòu)函數(shù),編譯系統(tǒng)會(huì)自動(dòng)為和這個(gè)類(lèi)生成一個(gè)默認(rèn)的析構(gòu)函數(shù)。
析構(gòu)函數(shù)的定義:
//1.類(lèi)中定義 2.類(lèi)中聲明,類(lèi)外定義
[類(lèi)名::]~析構(gòu)函數(shù)名()
{
函數(shù)體;
}13.6 對(duì)象指針
對(duì)象指針的聲明和使用
類(lèi)名 *對(duì)象指針名; 對(duì)象指針 = &對(duì)象名; //訪問(wèn)對(duì)象成員 對(duì)象指針->數(shù)據(jù)成員名 對(duì)象指針->成員函數(shù)名(參數(shù)列表) Person p(123, "yar"); Person* pp = &p; Person* pp2 = new Person(234,"yar") pp->show();
指向?qū)ο蟪蓡T的指針
數(shù)據(jù)成員類(lèi)型 *指針變量名 = &對(duì)象名.數(shù)據(jù)成員名; 函數(shù)類(lèi)型 (類(lèi)名::*指針變量名)(參數(shù)列表); 指針變量名=&類(lèi)名::成員函數(shù)名; (對(duì)象名.*指針變量名)(參數(shù)列表); Person p(123, "yar"); void(Person::*pfun)(); pfun = &Person::show; (p.*pfun)();
this指針
每個(gè)成員函數(shù)都有一個(gè)特殊的指針this,它始終指向當(dāng)前被調(diào)用的成員函數(shù)操作的對(duì)象
class Person
{
public:
Person(int = 0,string = "張三");
void show();
private:
int age;
string name;
};
Person::Person(int a, string s):age(a),name(s)
{
cout << a << " " << s << endl;
}
void Person::show()
{
cout << "age="<<this->age << endl;
cout << "name=" <<this->name << endl;
}13.7 靜態(tài)成員
以關(guān)鍵字static開(kāi)頭的成員為靜態(tài)成員,多個(gè)類(lèi)共享。
- static 成員變量屬于類(lèi),不屬于某個(gè)具體的對(duì)象
- 靜態(tài)成員函數(shù)只能訪問(wèn)類(lèi)中靜態(tài)數(shù)據(jù)成員
靜態(tài)數(shù)據(jù)成員
//類(lèi)內(nèi)聲明,類(lèi)外定義
class xxx
{
static 數(shù)據(jù)類(lèi)型 靜態(tài)數(shù)據(jù)成員名;
}
數(shù)據(jù)類(lèi)型 類(lèi)名::靜態(tài)數(shù)據(jù)成員名=初值
//訪問(wèn)
類(lèi)名::靜態(tài)數(shù)據(jù)成員名;
對(duì)象名.靜態(tài)數(shù)據(jù)成員名;
對(duì)象指針名->靜態(tài)數(shù)據(jù)成員名;靜態(tài)成員函數(shù)
//類(lèi)內(nèi)聲明,類(lèi)外定義
class xxx
{
static 返回值類(lèi)型 靜態(tài)成員函數(shù)名(參數(shù)列表);
}
返回值類(lèi)型 類(lèi)名::靜態(tài)成員函數(shù)名(參數(shù)列表)
{
函數(shù)體;
}
//訪問(wèn)
類(lèi)名::靜態(tài)成員函數(shù)名(參數(shù)列表);
對(duì)象名.靜態(tài)成員函數(shù)名(參數(shù)列表);
對(duì)象指針名->靜態(tài)成員函數(shù)名(參數(shù)列表);13.8 友元
借助友元(friend),可以使得其他類(lèi)中得成員函數(shù)以及全局范圍內(nèi)得函數(shù)訪問(wèn)當(dāng)前類(lèi)得private成員。
友元函數(shù)
- 友元函數(shù)不是類(lèi)的成員函數(shù),所以沒(méi)有this指針,必須通過(guò)參數(shù)傳遞對(duì)象。
- 友元函數(shù)中不能直接引用對(duì)象成員的名字,只能通過(guò)形參傳遞進(jìn)來(lái)的對(duì)象或?qū)ο笾羔榿?lái)引用該對(duì)象的成員。
//1.將非成員函數(shù)聲明為友元函數(shù)
class Person
{
public:
Person(int = 0,string = "張三");
friend void show(Person *pper);//將show聲明為友元函數(shù)
private:
int age;
string name;
};
Person::Person(int a, string s):age(a),name(s)
{
cout << a << " " << s << endl;
}
void show(Person *pper)
{
cout << "age="<< pper->age << endl;
cout << "name=" << pper->name << endl;
}
int main()
{;
Person *pp = new Person(234,"yar");
show(pp);
system("pause");
return 0;
}
//2.將其他類(lèi)的成員函數(shù)聲明為友元函數(shù)
//person中的成員函數(shù)可以訪問(wèn)MobilePhone中的私有成員變量
class MobilePhone;//提前聲明
//聲明Person類(lèi)
class Person
{
public:
Person(int = 0,string = "張三");
void show(MobilePhone *mp);
private:
int age;
string name;
};
//聲明MobilePhone類(lèi)
class MobilePhone
{
public:
MobilePhone();
friend void Person::show(MobilePhone *mp);
private:
int year;
int memory;
string name;
};
MobilePhone::MobilePhone()
{
year = 1;
memory = 4;
name = "iphone 6s";
}
Person::Person(int a, string s):age(a),name(s)
{
cout << a << " " << s << endl;
}
void Person::show(MobilePhone *mp)
{
cout << mp->year << "年 " << mp->memory << "G " << mp->name << endl;
}
int main()
{
Person *pp = new Person(234,"yar");
MobilePhone *mp = new MobilePhone;
pp->show(mp);
system("pause");
return 0;
}友元類(lèi)
當(dāng)一個(gè)類(lèi)為另一個(gè)類(lèi)的友元時(shí),稱(chēng)這個(gè)類(lèi)為友元類(lèi)。 友元類(lèi)的所有成員函數(shù)都是另一個(gè)類(lèi)中的友元成員。
語(yǔ)法形式:friend [class] 友元類(lèi)名
- 類(lèi)之間的友元關(guān)系不能傳遞
- 類(lèi)之間的友元關(guān)系是單向的
- 友元關(guān)系不能被繼承
class HardDisk
{
public:
HardDisk();
friend class Computer;
private:
int capacity;
int speed;
string brand;
};
HardDisk::HardDisk():capacity(128),speed(0),brand("三星"){
}
class Computer
{
public:
Computer(HardDisk hd);
void start();
private:
string userName;
string name;
int ram;
string cpu;
int osType;
HardDisk hardDisk;
};
Computer::Computer(HardDisk hd):userName("yar"),name("YAR-PC"),ram(16),cpu("i7-4710"),osType(64)
{
cout << "正在創(chuàng)建computer..." << endl;
this->hardDisk = hd;
this->hardDisk.speed = 5400;
cout << "硬盤(pán)轉(zhuǎn)動(dòng)...speed = " << this->hardDisk.speed << "轉(zhuǎn)/分鐘" << endl;
}
void Computer::start()
{
cout << hardDisk.brand << " " << hardDisk.capacity << "G" << hardDisk.speed << "轉(zhuǎn)/分鐘" << endl;
cout << "筆記本開(kāi)始運(yùn)行..." << endl;
}
int main()
{
HardDisk hd;
Computer cp(hd);
cp.start();
system("pause");
return 0;
}13.9 類(lèi)(class)與結(jié)構(gòu)體(struct)的區(qū)別
- 引入C語(yǔ)言的結(jié)構(gòu)體,是為了保證和c程序的兼容性。
- c語(yǔ)言中的結(jié)構(gòu)體不允許定義函數(shù)成員,且沒(méi)有訪問(wèn)控制權(quán)限的屬性。
- c++為結(jié)構(gòu)體引入了成員函數(shù),訪問(wèn)控制權(quán)限,繼承,多態(tài)等面向?qū)ο筇匦浴?/li>
- c語(yǔ)言中,空結(jié)構(gòu)體的大小為0,而C++中空結(jié)構(gòu)體大小為1。
- class中成員默認(rèn)是private,struct中的成員默認(rèn)是public。
- class繼承默認(rèn)是private繼承,而struct繼承默認(rèn)是public繼承。
- class可以使用模版,而struct不能。
舉個(gè)例子:
//結(jié)構(gòu)體默認(rèn)權(quán)限為public
struct person
{
void show();
string name;
int age;
};
int main()
{
person p;
p.name = "heiren";
p.age = 666;
p.show();
cout <<"name="<< p.name <<" age="<< p.age << endl;
system("pause");
return 0;
}將struct改為class,運(yùn)行報(bào)錯(cuò)。
14、繼承和派生
14.1 繼承和派生概述
繼承就是再一個(gè)已有類(lèi)的基礎(chǔ)上建立一個(gè)新類(lèi),已有的類(lèi)稱(chēng)基類(lèi)或父類(lèi),新建立的類(lèi)稱(chēng)為派生類(lèi)和子類(lèi);派生和繼承是一個(gè)概念,角度不同而已,繼承是兒子繼承父親的產(chǎn)業(yè),派生是父親把產(chǎn)業(yè)傳承給兒子。
一個(gè)基類(lèi)可以派生出多個(gè)派生類(lèi),一個(gè)派生類(lèi)可以繼承多個(gè)基類(lèi)
派生類(lèi)的聲明:
//繼承方式為可選項(xiàng),默認(rèn)為private,還有public,protected
class 派生類(lèi)名:[繼承方式]基類(lèi)名
{
派生類(lèi)新增加的成員聲明;
};繼承方式:
- public-基類(lèi)的public成員和protected成員的訪問(wèn)屬性保持不變,私有成員不可見(jiàn)。
- private-基類(lèi)的public成員和protected成員成為private成員,只能被派生類(lèi)的成員函數(shù)直接訪問(wèn),私有成員不可見(jiàn)。
- protected-基類(lèi)的public成員和protected成員成為protected成員,只能被派生類(lèi)的成員函數(shù)直接訪問(wèn),私有成員不可見(jiàn)。
| 繼承方式/基類(lèi)成員 | public成員 | protected成員 | private成員 |
|---|---|---|---|
| public | public | protected | 不可見(jiàn) |
| protected | protected | protected | 不可見(jiàn) |
| private | private | private | 不可見(jiàn) |
利用using關(guān)鍵字可以改變基類(lèi)成員再派生類(lèi)中的訪問(wèn)權(quán)限;using只能修改基類(lèi)中public和protected成員的訪問(wèn)權(quán)限。
class Base
{
public:
void show();
protected:
int aa;
double dd;
};
void Base::show(){
}
class Person:public Base
{
public:
using Base::aa;//將基類(lèi)的protected成員變成public
using Base::dd;//將基類(lèi)的protected成員變成public
private:
using Base::show;//將基類(lèi)的public成員變成private
string name;
};
int main()
{
Person *p = new Person();
p->aa = 12;
p->dd = 12.3;
p->show();//出錯(cuò)
delete p;
return 0;
}派生類(lèi)的構(gòu)造函數(shù)和析構(gòu)函數(shù)
- 先執(zhí)行基類(lèi)的構(gòu)造函數(shù),隨后執(zhí)行派生類(lèi)的構(gòu)造函數(shù)
- 先執(zhí)行派生類(lèi)的析構(gòu)函數(shù),再執(zhí)行基類(lèi)的析構(gòu)函數(shù)。
- 派生類(lèi)的構(gòu)造函數(shù):
派生類(lèi)名(總參數(shù)列表):基類(lèi)名(基類(lèi)參數(shù)列表),子對(duì)象名1(參數(shù)列表){構(gòu)造函數(shù)體;}
class Base
{
public:
Base(int, double);
~Base();
private:
int aa;
double dd;
};
Base::Base(int a, double d) :aa(a), dd(d)
{
cout << "Base Class 構(gòu)造函數(shù)!!!" << endl;
}
Base::~Base()
{
cout << "Base Class 析構(gòu)函數(shù)!!!" << endl;
}
class Person:public Base
{
public:
Person(int,double,string);
~Person();
private:
string name;
};
Person::Person(int a,double d,string str):Base(a,d),name(str)
{
cout << "Person Class 構(gòu)造函數(shù)!!!" << endl;
}
Person::~Person()
{
cout << "Person Class 析構(gòu)函數(shù)!!!" << endl;
}
int main()
{
cout << "創(chuàng)建Person對(duì)象..." << endl;
Person *p = new Person(1,2,"yar");
cout << "刪除Person對(duì)象...." << endl;
delete p;
system("pause");
return 0;
}
14.2 多繼承
一個(gè)派生類(lèi)同時(shí)繼承多個(gè)基類(lèi)的行為。
多繼承容易讓代碼邏輯復(fù)雜、思路混亂,一直備受爭(zhēng)議,中小型項(xiàng)目中較少使用,后來(lái)的 Java、C#、PHP 等干脆取消了多繼承。
多重繼承派生類(lèi)聲明的一般形式:
class 派生類(lèi)名:繼承方式1 基類(lèi)1,繼承方式2 基類(lèi)2
{
派生類(lèi)主體;
};多重繼承派生類(lèi)的構(gòu)造函數(shù):
派生類(lèi)名(總參數(shù)列表):基類(lèi)名1(基類(lèi)參數(shù)列表1),基類(lèi)名2(基類(lèi)參數(shù)列表2),
子對(duì)象名1,...(參數(shù)列表)
{
構(gòu)造函數(shù)體;
}`二義性問(wèn)題:多個(gè)基類(lèi)中有同名成員,出現(xiàn)訪問(wèn)不唯一的問(wèn)題。
- 1.
類(lèi)名::同名成員名; - 2.派生類(lèi)定義同名成員,訪問(wèn)的就是派生類(lèi)同名成員。
14.3 虛基類(lèi)
c++引入虛基類(lèi)使得派生類(lèi)再繼承間接共同基類(lèi)時(shí)只保留一份同名成員。
- 虛繼承的目的是讓某個(gè)類(lèi)做出聲明,承諾愿意共享它的基類(lèi)。其中,這個(gè)被共享的基類(lèi)就稱(chēng)為虛基類(lèi)(Virtual Base Class)。
- 派生類(lèi)的 同名成員 比虛基類(lèi)的 優(yōu)先級(jí)更高
虛基類(lèi)的聲明:class 派生類(lèi)名:virtual 繼承方式 基類(lèi)名
class A//虛基類(lèi)
{
protected:
int a;
};
class B: virtual public A
{
protected:
int b;
};
class C:virtual public A
{
protected:
int c;
};
class D:public B,public C
{
protected:
int d;
void show()
{
b = 123;
c = 23;
a = 1;
}
};- 如果 B 或 C 其中的一個(gè)類(lèi)定義了a,也不會(huì)有二義性,派生類(lèi)的a 比虛基類(lèi)的a 優(yōu)先級(jí)更高。
- 如果 B 和 C 中都定義了 a,那么D直接訪問(wèn)a 將產(chǎn)生二義性問(wèn)題。
應(yīng)用:c++中的iostream , istream , ostream,base_io
15、多態(tài)和虛函數(shù)
15.1 向上轉(zhuǎn)型
數(shù)據(jù)類(lèi)型的轉(zhuǎn)換,編譯器會(huì)將小數(shù)部分直接丟掉(不是四舍五入)
int a = 66.9;
printf("%d\n", a);//66
float b = 66;
printf("%f\n", b);//66.000000- 只能將將派生類(lèi)賦值給基類(lèi)(C++中稱(chēng)為向上轉(zhuǎn)型): 派生類(lèi)對(duì)象賦值給基類(lèi)對(duì)象、將派生類(lèi)指針賦值給基類(lèi)指針、將派生類(lèi)引用賦值給基類(lèi)引用
- 派生類(lèi)對(duì)象賦值給基類(lèi)對(duì)象,舍棄派生類(lèi)新增的成員;派生類(lèi)指針賦值給基類(lèi)指針,沒(méi)有拷貝對(duì)象的成員,也沒(méi)有修改對(duì)象本身的數(shù)據(jù),僅僅是改變了指針的指向;派生類(lèi)引用賦值給基類(lèi)引用,和指針的一樣。、
上轉(zhuǎn)型后通過(guò)基類(lèi)的對(duì)象、指針、引用只能訪問(wèn)從基類(lèi)繼承過(guò)去的成員(包括成員變量和成員函數(shù)),不能訪問(wèn)派生類(lèi)新增的成員
15.2 多態(tài)
不同的對(duì)象可以使用同一個(gè)函數(shù)名調(diào)用不同內(nèi)容的函數(shù)。
- 靜態(tài)多態(tài)性-在程序編譯時(shí)系統(tǒng)就決定調(diào)用哪個(gè)函數(shù),比如函數(shù)重載和靜態(tài)多態(tài)性
- 動(dòng)態(tài)多態(tài)性-在程序運(yùn)行過(guò)程中動(dòng)態(tài)確定調(diào)用那個(gè)函數(shù),通過(guò)虛函數(shù)實(shí)現(xiàn)的。
15.3 虛函數(shù)
實(shí)現(xiàn)程序多態(tài)性的一個(gè)重要手段,使用基類(lèi)對(duì)象指針訪問(wèn)派生類(lèi)對(duì)象的同名函數(shù)。
- 將基類(lèi)中的函數(shù)聲明為虛函數(shù),派生類(lèi)中的同名函數(shù)自動(dòng)為虛函數(shù)。
- 聲明形式:
virtual 函數(shù)類(lèi)型 函數(shù)名 (參數(shù)列表); - 構(gòu)造函數(shù)不能聲明為虛函數(shù),析構(gòu)函數(shù)可以聲明為虛函數(shù)。
class A
{
public:
virtual void show()
{
cout << "A show" << endl;
}
};
class B: public A
{
public:
void show()
{
cout << "B show" << endl;
}
};
int main()
{
B b;
b.show();//B show
A *pA = &b;
pA->show();//B show 如果show方法前沒(méi)用virtual聲明為虛函數(shù),這里會(huì)輸出A show
system("pause");
return 0;
}15.4 純虛函數(shù)
在基類(lèi)中不執(zhí)行具體的操作,只為派生類(lèi)提供統(tǒng)一結(jié)構(gòu)的虛函數(shù),將其聲明為虛函數(shù)。
class A
{
public:
virtual void show() = 0;
};
class B: public A
{
public:
void show()
{
cout << "B show" << endl;
}
};抽象類(lèi):包含純虛函數(shù)的類(lèi)稱(chēng)為抽象類(lèi)。由于純虛函數(shù)不能被調(diào)用,所以不能利用抽象類(lèi)創(chuàng)建對(duì)象,又稱(chēng)抽象基類(lèi)。
16、運(yùn)算符重載
所謂重載,就是賦予新的含義。函數(shù)重載(Function Overloading)可以讓一個(gè)函數(shù)名有多種功能,在不同情況下進(jìn)行不同的操作。運(yùn)算符重載(Operator Overloading)也是一個(gè)道理,同一個(gè)運(yùn)算符可以有不同的功能。
運(yùn)算符重載是通過(guò)函數(shù)實(shí)現(xiàn)的,它本質(zhì)上是函數(shù)重載。
允許重載的運(yùn)算符
| 運(yùn)算符名稱(chēng) | 運(yùn)算符 |
|---|---|
| 雙目算術(shù)運(yùn)算符 | +、-、*、、、% |
| 關(guān)系運(yùn)算符 | ==、!=、<、>、<=、>= |
| 邏輯運(yùn)算符 | ||、&&、! |
| 單目運(yùn)算符 | +、-、*(指針)、&(取地址) |
| 自增自減運(yùn)算符 | ++、– |
| 位運(yùn)算符 | |、&、-、……、<<、>> |
| 賦值運(yùn)算符 | =、+=、-=、*=、/=、%=、&=、!=、^=、<<= 、>>= |
| 空間分配和釋放 | new、delete、new[]、delete[] |
| 其他運(yùn)算符 | ()(函數(shù)調(diào)用) 、->(成員訪問(wèn))、->*(成員指針訪問(wèn))、,(逗號(hào))、 |
不允許重載的運(yùn)算符
| 運(yùn)算符名稱(chēng) | 運(yùn)算符 |
|---|---|
| 成員訪問(wèn)運(yùn)算符 | . |
| 成員指針訪問(wèn)運(yùn)算符 | . * |
| 域運(yùn)算符 | :: |
| 長(zhǎng)度運(yùn)算符 | sizeof() |
| 條件運(yùn)算符 | ?: |
16.1 定義
重載運(yùn)算符遵循的規(guī)則:
- 不可以自己定義新的運(yùn)算符,只能對(duì)已有的C++運(yùn)算符重載。
- 不能改變運(yùn)算符運(yùn)算對(duì)象的個(gè)數(shù)。
- 不能改變運(yùn)算符的優(yōu)先級(jí)和結(jié)合性
- 應(yīng)與標(biāo)準(zhǔn)類(lèi)型運(yùn)算功能相似,避免影響可讀性。
一般格式:
函數(shù)類(lèi)型 operator運(yùn)算符(參數(shù)列表)
{
函數(shù)體
}
//舉個(gè)栗子:定義一個(gè)向量類(lèi),通過(guò)運(yùn)算符重載,可以用+進(jìn)行運(yùn)算。
class Vector3
{
public:
Vector3();
Vector3(double x,double y,double z);
public:
Vector3 operator+(const Vector3 &A)const;
void display()const;
private:
double m_x;
double m_y;
double m_z;
};
Vector3::Vector3() :m_x(0.0), m_y(0.0), m_z(0.0) {}
Vector3::Vector3(double x, double y,double z) : m_x(x), m_y(y), m_z(z) {}
//運(yùn)算符重載
Vector3 Vector3::operator+(const Vector3 &A) const
{
Vector3 B;
B.m_x = this->m_x + A.m_x;
B.m_y = this->m_y + A.m_y;
B.m_z = this->m_z + A.m_z;
return B;
}
void Vector3::display()const
{
cout<<"(" << m_x << "," << m_y << "," << m_z << ")" << endl;
}16.2 形式
運(yùn)算符重載的形式有兩種:重載函數(shù)作為類(lèi)的成員,重載函數(shù)作為類(lèi)的友元函數(shù)
根據(jù)運(yùn)算符操作數(shù)的不同:雙目運(yùn)算符作為類(lèi)成員函數(shù),單目運(yùn)算符作為類(lèi)的成員函數(shù),雙目運(yùn)算符作為類(lèi)的友員函數(shù),單目運(yùn)算符作為類(lèi)的友元函數(shù)。
- 雙目運(yùn)算符作為友元函數(shù)時(shí)需要制定兩個(gè)參數(shù)。
- 運(yùn)算符重載函數(shù)作為類(lèi)成員函數(shù)可以顯式調(diào)用。
class Vector3
{
public:
Vector3();
Vector3(double x,double y,double z);
public:
Vector3 operator+(const Vector3 &A)const;
Vector3 operator++();
friend Vector3 operator-(const Vector3 &v1, const Vector3 &v2);
friend Vector3 operator--(Vector3 &v);
void display()const;
private:
double m_x;
double m_y;
double m_z;
};
Vector3::Vector3() :m_x(0.0), m_y(0.0), m_z(0.0) {}
Vector3::Vector3(double x, double y,double z) : m_x(x), m_y(y), m_z(z) {}
//運(yùn)算符重載
Vector3 Vector3::operator+(const Vector3 &A) const
{
Vector3 B;
B.m_x = this->m_x + A.m_x;
B.m_y = this->m_y + A.m_y;
B.m_z = this->m_z + A.m_z;
return B;
}
Vector3 Vector3::operator++()
{
this->m_x ++;
this->m_y ++;
this->m_z ++;
return *this;
}
void Vector3::display()const
{
cout<<"(" << m_x << "," << m_y << "," << m_z << ")" << endl;
}
Vector3 operator-(const Vector3 &v1,const Vector3 &v2)
{
Vector3 B(v1.m_x - v2.m_x, v1.m_y - v2.m_y, v1.m_z - v2.m_z);
return B;
}
Vector3 operator--( Vector3 &v)
{
v.m_x--;
v.m_y--;
v.m_z --;
return v;
}
int main()
{
Vector3 v1(1, 2, 3);
Vector3 v2(2, 3, 2);
++v1;//v1.operator++(); 作為類(lèi)成員函數(shù)可以顯式調(diào)用
v1.display();
--v2;
v2.display();
Vector3 v3 = v1 + v2;// v1.operator+(v2);作為類(lèi)成員函數(shù)可以顯式調(diào)用
v3.display();
Vector3 v4 = v1 - v2;
v4.display();
return 0;
}16.3 常用運(yùn)算符的重載
1.自增自減:
//前置運(yùn)算符 ++a --a operator++() operator--() operator++(Vector3 &v) operator--(Vector3 &v) //后置運(yùn)算符 a-- a++ operator++(int) operator--(int) operator++(Vector3 &v,int) operator--(Vector3 &v,int)
2.賦值運(yùn)算符:
String& String::operator=(String &s)
{
if(this!=&s)
{
delete[] str;
int length = strlen(s.str);
str = new char[length+1];
strcpy(str,s.str);
}
return (*this)
}3.輸入\輸出運(yùn)算符重載
friend ostream &operator<<( ostream &output,
const Vector3 &v )
{
output << "F : " <<v.m_x<< " I : " << v.m_y<<v.m_z;
return output;
}
friend istream &operator>>( istream &input, Vector3 &v )
{
input >> v.m_x>> v.m_y>>v.m_z;
return input;
}16.4 實(shí)現(xiàn)類(lèi)型轉(zhuǎn)換
- 不指定函數(shù)類(lèi)型和參數(shù),返回值的類(lèi)型由類(lèi)型名來(lái)確定。
- 類(lèi)型轉(zhuǎn)換函數(shù)只能作為成員函數(shù),不能作為友元函數(shù)。
類(lèi)型轉(zhuǎn)換函數(shù)的一般形式:
operator 類(lèi)型名()
{
轉(zhuǎn)換語(yǔ)句;
}
class Vector3
{
public:
Vector3();
Vector3(double x,double y,double z);
public:
Vector3 operator+(const Vector3 &A)const;
Vector3 operator++();
friend Vector3 operator-(const Vector3 &v1, const Vector3 &v2);
friend Vector3 operator--(Vector3 &v,int);
operator double()
{
return m_x + m_y + m_z;
}
void display()const;
private:
double m_x;
double m_y;
double m_z;
};
int main()
{
Vector3 v1(1, 2, 3);
double d = v1;
cout << d << endl;//6
return 0;
}17、IO流
流-一連串連續(xù)不斷的數(shù)據(jù)集合。
17.1 流類(lèi)和對(duì)象
- 輸入流-從輸入設(shè)備流向內(nèi)存的流。
- 輸出流-從內(nèi)存流出設(shè)備的流。
- 內(nèi)存緩沖區(qū)-用來(lái)存放流中的數(shù)據(jù)。
輸入輸出流程:鍵盤(pán)輸入=》鍵盤(pán)緩沖區(qū)=(回車(chē)觸發(fā))》程序的輸入緩沖區(qū)=》‘>>’提取數(shù)據(jù)
輸出緩沖區(qū)=(緩沖滿(mǎn)或endl)》‘<<’送到 顯示器顯示
輸入/輸出流類(lèi):
iostream:ios ,istream,ostream,iostream
fstream:ifstream,ofstream,fstream
strstream:istrstream,ostrstream,strstream
- istream 是用于輸入的流類(lèi),cin 就是該類(lèi)的對(duì)象。
- ostream 是用于輸出的流類(lèi),cout 就是該類(lèi)的對(duì)象。
- ifstream 是用于從文件讀取數(shù)據(jù)的類(lèi)。
- ofstream 是用于向文件寫(xiě)入數(shù)據(jù)的類(lèi)。
- iostream 是既能用于輸入,又能用于輸出的類(lèi)。
- fstream 是既能從文件讀取數(shù)據(jù),又能向文件寫(xiě)入數(shù)據(jù)的類(lèi)。
- istrstream 輸入字符串類(lèi)
- ostrstream 輸出字符串類(lèi)
- strstream 輸入輸出字符串流類(lèi)

17.2 標(biāo)準(zhǔn)輸入輸出流
C++的輸入/輸出流庫(kù)(iostream)中定義了4個(gè)標(biāo)準(zhǔn)流對(duì)象:cin(標(biāo)準(zhǔn)輸入流-鍵盤(pán)),cout(標(biāo)準(zhǔn)輸出流-屏幕),cerr(標(biāo)準(zhǔn)錯(cuò)誤流-屏幕),clog(標(biāo)準(zhǔn)錯(cuò)誤流-屏幕)
- cerr 不使用緩沖區(qū),直接向顯示器輸出信息;而輸出到 clog 中的信息會(huì)先被存放到緩沖區(qū),緩沖區(qū)滿(mǎn)或者刷新時(shí)才輸出到屏幕。
- cout 是 ostream 類(lèi)的對(duì)象,ostream 類(lèi)的無(wú)參構(gòu)造函數(shù)和復(fù)制構(gòu)造函數(shù)都是私有的,所以無(wú)法定義 ostream 類(lèi)的對(duì)象。
- 使用>>提取數(shù)據(jù)時(shí),系統(tǒng)會(huì)跳過(guò)空格,制表符,換行符等空白字符。所以一組變量輸入值時(shí),可用這些隔開(kāi)。
- 輸入字符串,也是跳過(guò)空白字符,會(huì)在串尾加上字符串結(jié)束標(biāo)志\0。
int x; double y; cin>>x>>y; //輸入 22 66.0 兩個(gè)數(shù)之間可以用空格、制表符和回車(chē)分隔數(shù)據(jù) char str[10]; cin>>str;//hei ren 字符串中只有hei\0
輸入流中的成員函數(shù)
get函數(shù):cin.get(),cin.get(ch)(成功返回非0值,否則返回0),cin.get(字符數(shù)組(或字符指針),字符個(gè)數(shù)n,終止字符)
char c = cin.get();//獲取一個(gè)字符
while ((c = cin.get()) != EOF)//循環(huán)讀取,直到換行
{
cout << c;
}
char ch;
cin.get(ch);
while (cin.get(ch))//讀取成功循環(huán)
{
cout << ch;
}
char arr[5];
cin.get(arr, 5, '\n');//輸入 heiren 結(jié)果 heir\0getline函數(shù):cin.getline(字符數(shù)組(或字符指針),字符個(gè)數(shù)n,終止標(biāo)志字符)
讀取字符知道終止字符或者讀取n-1個(gè)字符,賦值給指定字符數(shù)組(或字符指針)
char arr0[30],arr1[30],arr2[40]; cin>>arr0;//遇到空格、制表符或回車(chē)結(jié)束 "Heiren" cin.getline(arr1,30);//字符數(shù)最多為29個(gè),遇到回車(chē)結(jié)束 " Hello World" cin.getline(arr2,40,'*');//最多為39個(gè),遇到*結(jié)束 "yar" //輸入 Heiren Hello World //yar*123
cin.peek() 不會(huì)跳過(guò)輸入流中的空格、回車(chē)符。在輸入流已經(jīng)結(jié)束的情況下,cin.peek() 返回 EOF。
ignore(int n =1, int delim = EOF)
int n;
cin.ignore(5, 'Y');//跳過(guò)前5個(gè)字符或Y之前的字符,‘Y'優(yōu)先
cin >> n;
//輸入1234567 -> 67 1234567Y345->345
//輸入2020.2.23
int year,month,day;
cin >> year ;
cin.ignore() >> month ; //用ignore跳過(guò) '.'
cin.ignore() >> day;
cin.ignore(); //跳過(guò)行末 '\n'
cout<< setfill('0') << setw(2) << month ;//設(shè)置填充字符'\0',輸出寬度2
cout << "-" << setw(2) << day << "-" << setw(4) << year << endl;putback(char c),可以將一個(gè)字符插入輸入流的最前面。
輸出流對(duì)象
- 插入endl-輸出所有數(shù)據(jù),插入換行符,清空緩沖區(qū)
- \n-輸出換行,不清空緩沖區(qū)
- cout.put(參數(shù)) 輸出單個(gè)字符(可以時(shí)字符也可以是ASII碼)
格式化輸出
iomanip 中定義的流操作算子:
*不是算子的一部分,星號(hào)表示在沒(méi)有使用任何算子的情況下,就等效于使用了該算子,例如,在默認(rèn)情況下,整數(shù)是用十進(jìn)制形式輸出的,等效于使用了 dec 算子
| 流操縱算子 | 作 用 |
|---|---|
| *dec | 以十進(jìn)制形式輸出整數(shù) 常用 |
| hex | 以十六進(jìn)制形式輸出整數(shù) |
| oct | 以八進(jìn)制形式輸出整數(shù) |
| fixed | 以普通小數(shù)形式輸出浮點(diǎn)數(shù) |
| scientific | 以科學(xué)計(jì)數(shù)法形式輸出浮點(diǎn)數(shù) |
| left | 左對(duì)齊,即在寬度不足時(shí)將填充字符添加到右邊 |
| *right | 右對(duì)齊,即在寬度不足時(shí)將填充字符添加到左邊 |
| setbase(b) | 設(shè)置輸出整數(shù)時(shí)的進(jìn)制,b=8、10 或 16 |
| setw(w) | 指定輸出寬度為 w 個(gè)字符,或輸人字符串時(shí)讀入 w 個(gè)字符 |
| setfill© | 在指定輸出寬度的情況下,輸出的寬度不足時(shí)用字符 c 填充(默認(rèn)情況是用空格填充) |
| setprecision(n) | 設(shè)置輸出浮點(diǎn)數(shù)的精度為 n。在使用非 fixed 且非 scientific 方式輸出的情況下,n 即為有效數(shù)字最多的位數(shù),如果有效數(shù)字位數(shù)超過(guò) n,則小數(shù)部分四舍五人,或自動(dòng)變?yōu)榭茖W(xué)計(jì) 數(shù)法輸出并保留一共 n 位有效數(shù)字。在使用 fixed 方式和 scientific 方式輸出的情況下,n 是小數(shù)點(diǎn)后面應(yīng)保留的位數(shù)。 |
| setiosflags(flag) | 將某個(gè)輸出格式標(biāo)志置為 1 |
| resetiosflags(flag) | 將某個(gè)輸出格式標(biāo)志置為 0 |
| boolapha | 把 true 和 false 輸出為字符串 不常用 |
| *noboolalpha | 把 true 和 false 輸出為 0、1 |
| showbase | 輸出表示數(shù)值的進(jìn)制的前綴 |
| *noshowbase | 不輸出表示數(shù)值的進(jìn)制.的前綴 |
| showpoint | 總是輸出小數(shù)點(diǎn) |
| *noshowpoint | 只有當(dāng)小數(shù)部分存在時(shí)才顯示小數(shù)點(diǎn) |
| showpos | 在非負(fù)數(shù)值中顯示 + |
| *noshowpos | 在非負(fù)數(shù)值中不顯示 + |
| *skipws | 輸入時(shí)跳過(guò)空白字符 |
| noskipws | 輸入時(shí)不跳過(guò)空白字符 |
| uppercase | 十六進(jìn)制數(shù)中使用 A~E。若輸出前綴,則前綴輸出 0X,科學(xué)計(jì)數(shù)法中輸出 E |
| *nouppercase | 十六進(jìn)制數(shù)中使用 a~e。若輸出前綴,則前綴輸出 0x,科學(xué)計(jì)數(shù)法中輸出 e。 |
| internal | 數(shù)值的符號(hào)(正負(fù)號(hào))在指定寬度內(nèi)左對(duì)齊,數(shù)值右對(duì) 齊,中間由填充字符填充。 |
流操作算子使用方法:cout << hex << 12 << "," << 24;//c,18
setiosflags() 算子
setiosflags() 算子實(shí)際上是一個(gè)庫(kù)函數(shù),它以一些標(biāo)志作為參數(shù),這些標(biāo)志可以是在 iostream 頭文件中定義的以下幾種取值,它們的含義和同名算子一樣。
| 標(biāo) 志 | 作 用 |
|---|---|
| ios::left | 輸出數(shù)據(jù)在本域?qū)挿秶鷥?nèi)向左對(duì)齊 |
| ios::right | 輸出數(shù)據(jù)在本域?qū)挿秶鷥?nèi)向右對(duì)齊 |
| ios::internal | 數(shù)值的符號(hào)位在域?qū)拑?nèi)左對(duì)齊,數(shù)值右對(duì)齊,中間由填充字符填充 |
| ios::dec | 設(shè)置整數(shù)的基數(shù)為 10 |
| ios::oct | 設(shè)置整數(shù)的基數(shù)為 8 |
| ios::hex | 設(shè)置整數(shù)的基數(shù)為 16 |
| ios::showbase | 強(qiáng)制輸出整數(shù)的基數(shù)(八進(jìn)制數(shù)以 0 開(kāi)頭,十六進(jìn)制數(shù)以 0x 打頭) |
| ios::showpoint | 強(qiáng)制輸出浮點(diǎn)數(shù)的小點(diǎn)和尾數(shù) 0 |
| ios::uppercase | 在以科學(xué)記數(shù)法格式 E 和以十六進(jìn)制輸出字母時(shí)以大寫(xiě)表示 |
| ios::showpos | 對(duì)正數(shù)顯示“+”號(hào) |
| ios::scientific | 浮點(diǎn)數(shù)以科學(xué)記數(shù)法格式輸出 |
| ios::fixed | 浮點(diǎn)數(shù)以定點(diǎn)格式(小數(shù)形式)輸出 |
| ios::unitbuf | 每次輸出之后刷新所有的流 |
| ios::stdio | 每次輸出之后清除 stdout, stderr |
多個(gè)標(biāo)志可以用|運(yùn)算符連接,表示同時(shí)設(shè)置。例如:
cout << setiosflags(ios::scientific|ios::showpos) << 12.34;//+1.234000e+001
如果兩個(gè)相互矛盾的標(biāo)志同時(shí)被設(shè)置,結(jié)果可能就是兩個(gè)標(biāo)志都不起作用,應(yīng)該用 resetiosflags 清除原先的標(biāo)志
cout << setiosflags(ios::fixed) << 12.34 << endl; cout << resetiosflags(ios::fixed) << setiosflags(ios::scientific | ios::showpos) << 12.34 << endl;
ostream 類(lèi)中的成員函數(shù):
| 成員函數(shù) | 作用相同的流操縱算子 | 說(shuō)明 |
|---|---|---|
| precision(n) | setprecision(n) | 設(shè)置輸出浮點(diǎn)數(shù)的精度為 n。 |
| width(w) | setw(w) | 指定輸出寬度為 w 個(gè)字符。 |
| fill© | setfill © | 在指定輸出寬度的情況下,輸出的寬度不足時(shí)用字符 c 填充(默認(rèn)情況是用空格填充)。 |
| setf(flag) | setiosflags(flag) | 將某個(gè)輸出格式標(biāo)志置為 1。 |
| unsetf(flag) | resetiosflags(flag) | 將某個(gè)輸出格式標(biāo)志置為 0。 |
setf 和 unsetf 函數(shù)用到的flag,與 setiosflags 和 resetiosflags 用到的完全相同。
cout.setf(ios::scientific); cout.precision(8); cout << 12.23 << endl;//1.22300000e+001
18、文件操作
文件-指存儲(chǔ)在外部介質(zhì)上的數(shù)據(jù)集合,文件按照數(shù)據(jù)的組織形式不一樣,分為兩種:ASCII文件(文本/字符),二進(jìn)制文件(內(nèi)部格式/字節(jié))
ASCII文件輸出還是二進(jìn)制文件,數(shù)據(jù)形式一樣,對(duì)于數(shù)值數(shù)據(jù),輸出不同
18.1 文件類(lèi)和對(duì)象
C++ 標(biāo)準(zhǔn)類(lèi)庫(kù)中有三個(gè)類(lèi)可以用于文件操作,它們統(tǒng)稱(chēng)為文件流類(lèi)。這三個(gè)類(lèi)是:
- ifstream:輸入流類(lèi),用于從文件中讀取數(shù)據(jù)。
- ofstream:輸出流類(lèi),用于向文件中寫(xiě)人數(shù)據(jù)。
- fstream:輸入/輸出流類(lèi),既可用于從文件中讀取數(shù)據(jù),又可用于 向文件中寫(xiě)人數(shù)據(jù)。
文件流對(duì)象定義:
#include <fstream> ifstream in; ofstream out; fstream inout;
18.2 打開(kāi)文件
打開(kāi)文件的目的:建立對(duì)象與文件的關(guān)聯(lián),指明文件使用方式
打開(kāi)文件的兩種方式:open函數(shù)和構(gòu)造函數(shù)
open函數(shù):void open(const char* szFileName, int mode);
| 模式標(biāo)記 | 適用對(duì)象 | 作用 |
|---|---|---|
| ios::in | ifstream fstream | 打開(kāi)文件用于讀取數(shù)據(jù)。如果文件不存在,則打開(kāi)出錯(cuò)。 |
| ios::out | ofstream fstream | 打開(kāi)文件用于寫(xiě)入數(shù)據(jù)。如果文件不存在,則新建該文件;如 果文件原來(lái)就存在,則打開(kāi)時(shí)清除原來(lái)的內(nèi)容。 |
| ios::app | ofstream fstream | 打開(kāi)文件,用于在其尾部添加數(shù)據(jù)。如果文件不存在,則新建該文件。 |
| ios::ate | ifstream | 打開(kāi)一個(gè)已有的文件,并將文件讀指針指向文件末尾(讀寫(xiě)指 的概念后面解釋?zhuān)?。如果文件不存在,則打開(kāi)出錯(cuò)。 |
| ios:: trunc | ofstream | 單獨(dú)使用時(shí)與 ios:: out 相同。 |
| ios::binary | ifstream ofstream fstream | 以二進(jìn)制方式打開(kāi)文件。若不指定此模式,則以文本模式打開(kāi)。 |
| ios::in | ios::out | fstream | 打開(kāi)已存在的文件,既可讀取其內(nèi)容,也可向其寫(xiě)入數(shù)據(jù)。文件剛打開(kāi)時(shí),原有內(nèi)容保持不變。如果文件不存在,則打開(kāi)出錯(cuò)。 |
| ios::in | ios::out | ofstream | 打開(kāi)已存在的文件,可以向其寫(xiě)入數(shù)據(jù)。文件剛打開(kāi)時(shí),原有內(nèi)容保持不變。如果文件不存在,則打開(kāi)出錯(cuò)。 |
| ios::in | ios::out | ios::trunc | fstream | 打開(kāi)文件,既可讀取其內(nèi)容,也可向其寫(xiě)入數(shù)據(jù)。如果文件本來(lái)就存在,則打開(kāi)時(shí)清除原來(lái)的內(nèi)容;如果文件不存在,則新建該文件。 |
ios::binary 可以和其他模式標(biāo)記組合使用,例如:
- ios::in | ios::binary表示用二進(jìn)制模式,以讀取的方式打開(kāi)文件。
- ios::out | ios::binary表示用二進(jìn)制模式,以寫(xiě)入的方式打開(kāi)文件。
流類(lèi)的構(gòu)造函數(shù)
eg:ifstream::ifstream (const char* szFileName, int mode = ios::in, int);
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream inFile("c:\\tmp\\test.txt", ios::in);
if (inFile)
inFile.close();
else
cout << "test.txt doesn't exist" << endl;
ofstream oFile("test1.txt", ios::out);
if (!oFile)
cout << "error 1";
else
oFile.close();
fstream oFile2("tmp\\test2.txt", ios::out | ios::in);
if (!oFile2)
cout << "error 2";
else
oFile.close();
return 0;
}18.3 文本文件的讀寫(xiě)
對(duì)于文本文件,可以使用 cin、cout 讀寫(xiě)。
eg:編寫(xiě)一個(gè)程序,將文件 i.txt 中的整數(shù)倒序輸出到 o.txt。(12 34 56 78 90 -> 90 78 56 34 12)
#include <iostream>
#include <fstream>
using namespace std;
int arr[100];
int main()
{
int num = 0;
ifstream inFile("i.txt", ios::in);//文本模式打開(kāi)
if (!inFile)
return 0;//打開(kāi)失敗
ofstream outFile("o.txt",ios::out);
if (!outFile)
{
outFile.close();
return 0;
}
int x;
while (inFile >> x)
arr[num++] = x;
for (int i = num - 1; i >= 0; i--)
outFile << arr[i] << " ";
inFile.close();
outFile.close();
return 0;
}18.4 二進(jìn)制文件的讀寫(xiě)
- 用文本方式存儲(chǔ)信息不但浪費(fèi)空間,而且不便于檢索。
- 二進(jìn)制文件中,信息都占用 sizeof(對(duì)象名) 個(gè)字節(jié);文本文件中類(lèi)的成員數(shù)據(jù)所占用的字節(jié)數(shù)不同,占用空間一般比二進(jìn)制的大。
ostream::write 成員函數(shù):ostream & write(char* buffer, int count);
class Person
{
public:
char m_name[20];
int m_age;
};
int main()
{
Person p;
ofstream outFile("o.bin", ios::out | ios::binary);
while (cin >> p.m_name >> p.m_age)
outFile.write((char*)&p, sizeof(p));//強(qiáng)制類(lèi)型轉(zhuǎn)換
outFile.close();
//heiren 燙燙燙燙燙燙啼
return 0;
}istream::read 成員函數(shù):istream & read(char* buffer, int count);
Person p;
ifstream inFile("o.bin", ios::in | ios::binary); //二進(jìn)制讀方式打開(kāi)
if (!inFile)
return 0;//打開(kāi)文件失敗
while (inFile.read((char *)&p, sizeof(p)))
cout << p.m_name << " " << p.m_age << endl;
inFile.close();文件流類(lèi)的 put 和 get 成員函數(shù)
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream inFile("a.txt", ios::binary | ios::in);
if (!inFile)
return 0;
ofstream outFile("b.txt", ios::binary | ios::out);
if (!outFile)
{
inFile.close();
return 0;
}
char c;
while (inFile.get(c)) //每讀一個(gè)字符
outFile.put(c); //寫(xiě)一個(gè)字符
outFile.close();
inFile.close();
return 0;
}一個(gè)字節(jié)一個(gè)字節(jié)地讀寫(xiě),不如一次讀寫(xiě)一片內(nèi)存區(qū)域快。每次讀寫(xiě)的字節(jié)數(shù)最好是 512 的整數(shù)倍
18.5 移動(dòng)和獲取文件讀寫(xiě)指針
- ifstream 類(lèi)和 fstream 類(lèi)有 seekg 成員函數(shù),可以設(shè)置文件讀指針的位置;
- ofstream 類(lèi)和 fstream 類(lèi)有 seekp 成員函數(shù),可以設(shè)置文件寫(xiě)指針的位置。
- ifstream 類(lèi)和 fstream 類(lèi)還有 tellg 成員函數(shù),能夠返回文件讀指針的位置;
- ofstream 類(lèi)和 fstream 類(lèi)還有 tellp 成員函數(shù),能夠返回文件寫(xiě)指針的位置。
函數(shù)原型
ostream & seekp (int offset, int mode); istream & seekg (int offset, int mode); //mode有三種:ios::beg-開(kāi)頭往后offset(>=0)字節(jié) ios::cur-當(dāng)前往前(<=0)/后(>=0)offset字節(jié) ios::end-末尾往前(<=0)offect字節(jié) int tellg(); int tellp(); //seekg 函數(shù)將文件讀指針定位到文件尾部,再用 tellg 函數(shù)獲取文件讀指針的位置,此位置即為文件長(zhǎng)度
舉個(gè)栗子:折半查找文件,name等于“Heiren”
#include <iostream>
#include <fstream>
//#include <vector>
//#include<cstring>
using namespace std;
class Person
{
public:
char m_name[20];
int m_age;
};
int main()
{
Person p;
ifstream ioFile("p.bin", ios::in | ios::out);//用既讀又寫(xiě)的方式打開(kāi)
if (!ioFile)
return 0;
ioFile.seekg(0, ios::end); //定位讀指針到文件尾部,以便用以后tellg 獲取文件長(zhǎng)度
int L = 0, R; // L是折半查找范圍內(nèi)第一個(gè)記錄的序號(hào)
// R是折半查找范圍內(nèi)最后一個(gè)記錄的序號(hào)
R = ioFile.tellg() / sizeof(Person) - 1;
do {
int mid = (L + R) / 2;
ioFile.seekg(mid *sizeof(Person), ios::beg);
ioFile.read((char *)&p, sizeof(p));
int tmp = strcmp(p.m_name, "Heiren");
if (tmp == 0)
{
cout << p.m_name << " " << p.m_age;
break;
}
else if (tmp > 0)
R = mid - 1;
else
L = mid + 1;
} while (L <= R);
ioFile.close();
system("pause");
return 0;
}18.6 文本文件和二進(jìn)制文件打開(kāi)方式的區(qū)別
- UNIX/Linux 平臺(tái)中,用文本方式或二進(jìn)制方式打開(kāi)文件沒(méi)有任何區(qū)別。
- 在 UNIX/Linux 平臺(tái)中,文本文件以\n(ASCII 碼為 0x0a)作為換行符號(hào);而在 Windows 平臺(tái)中,文本文件以連在一起的\r\n(\r的 ASCII 碼是 0x0d)作為換行符號(hào)。
- 在 Windows 平臺(tái)中,如果以文本方式打開(kāi)文件,當(dāng)讀取文件時(shí),系統(tǒng)會(huì)將文件中所有的\r\n轉(zhuǎn)換成一個(gè)字符\n,如果文件中有連續(xù)的兩個(gè)字節(jié)是 0x0d0a,則系統(tǒng)會(huì)丟棄前面的 0x0d 這個(gè)字節(jié),只讀入 0x0a。當(dāng)寫(xiě)入文件時(shí),系統(tǒng)會(huì)將\n轉(zhuǎn)換成\r\n寫(xiě)入。
用二進(jìn)制方式打開(kāi)文件總是最保險(xiǎn)的。
19、泛型和模板
- 泛型程序設(shè)計(jì)在實(shí)現(xiàn)時(shí)不指定具體要操作的數(shù)據(jù)的類(lèi)型的程序設(shè)計(jì)方法的一種算法,指的是算法只要實(shí)現(xiàn)一遍,就能適用于多種數(shù)據(jù)類(lèi)型,優(yōu)勢(shì)在于代碼復(fù)用,減少重復(fù)代碼的編寫(xiě)。
- 模板是泛型的基礎(chǔ),是創(chuàng)建泛型類(lèi)或函數(shù)的藍(lán)圖或公式。
19.1 函數(shù)模板
函數(shù)模板的一般形式:
template<class T>或template<typename T>
函數(shù)類(lèi)型 函數(shù)名(參數(shù)列表)
{
函數(shù)體;
}
template<class T1,class T2,...>//class可以換成typename
函數(shù)類(lèi)型 函數(shù)名(參數(shù)列表)
{
函數(shù)體;
}
//舉個(gè)栗子
template<class T> T max(T a, T b)
{
return a > b ? a : b;
}
int main()
{
cout <<"max value is "<< max(12,34) << endl;//34
cout << "max value is " << max(12.4, 13.6) << endl;//13.6
cout << "max value is " << max(12.4, 13) << endl;//error 沒(méi)有與參數(shù)列表匹配的 函數(shù)模板 "max" 實(shí)例參數(shù)類(lèi)型為:(double, int)
return 0;
}19.2 類(lèi)模板
聲明了類(lèi)模板,就可以將類(lèi)型參數(shù)用于類(lèi)的成員函數(shù)和成員變量了。換句話(huà)說(shuō),原來(lái)使用 int、float、char 等內(nèi)置類(lèi)型的地方,都可以用類(lèi)型參數(shù)來(lái)代替。
類(lèi)模板的一般形式:
template<class T>//class可以換成typename 模板頭
class 類(lèi)名
{
函數(shù)定義;
};
//多個(gè)類(lèi)型參數(shù)和函數(shù)模板類(lèi)似,逗號(hào)隔開(kāi)舉個(gè)栗子:
template<typename T1, typename T2> // 模板頭 沒(méi)有分號(hào)
class Point {
public:
Point(T1 x, T2 y) : x(x), y(y) { }
public:
T1 getX() const; //成員函數(shù)后加const,聲明該函數(shù)內(nèi)部不會(huì)改變成員變量的值
void setX(T1 x);
T2 getY() const;
void setY(T2 y);
private:
T1 x;
T2 y;
};
template<typename T1, typename T2> //模板頭
T1 Point<T1, T2>::getX() const {
return x;
}
template<typename T1, typename T2>
void Point<T1, T2>::setX(T1 x) {
x = x;
}
template<typename T1, typename T2>
T2 Point<T1, T2>::getY() const {
return y;
}
template<typename T1, typename T2>
void Point<T1, T2>::setY(T2 y) {
y = y;
}
int main()
{
Point<int, double> p1(66, 20.5);
Point<int, char*> p2(10, "東經(jīng)33度");
Point<char*, char*> *p3 = new Point<char*, char*>("西經(jīng)12度", "北緯66度");
cout << "x=" << p1.getX() << ", y=" << p1.getY() << endl;
cout << "x=" << p2.getX() << ", y=" << p2.getY() << endl;
cout << "x=" << p3->getX() << ", y=" << p3->getY() << endl;
return 0;
}19.3 typename 和 class 的區(qū)別
在模板引入 c++ 后,采用class來(lái)定義模板參數(shù)類(lèi)型,后來(lái)為了避免 class 在聲明類(lèi)和模板的使用可能給人帶來(lái)混淆,所以引入了 typename 這個(gè)關(guān)鍵字。
- 模板定義語(yǔ)法中關(guān)鍵字 class 與 typename 的作用完全一樣。
- 不同的是typename 還有另外一個(gè)作用為:使用嵌套依賴(lài)類(lèi)型(nested depended name)
class MyClass
{
public:
typedef int LengthType;
LengthType getLength() const
{
return this->length;
}
void setLength(LengthType length)
{
this->length = length;
}
private:
LengthType length;
};
template<class T>
void MyMethod(T myclass)
{
//告訴 c++ 編譯器,typename 后面的字符串為一個(gè)類(lèi)型名稱(chēng),而不是成員函數(shù)或者成員變量
typedef typename T::LengthType LengthType; //
LengthType length = myclass.getLength();
cout << "length = " <<length<< endl;
}
int main()
{
MyClass my;
my.setLength(666);
MyMethod(my);//length = 666
return 0;
}19.4 強(qiáng)弱類(lèi)型語(yǔ)言和c++模板的那點(diǎn)貓膩
計(jì)算機(jī)編程語(yǔ)言可以根據(jù)在 "定義變量時(shí)是否要顯式地指明數(shù)據(jù)類(lèi)型"可以分為強(qiáng)類(lèi)型語(yǔ)言和弱類(lèi)型語(yǔ)言。
強(qiáng)類(lèi)型語(yǔ)言-在定義變量時(shí)需要顯式地指明數(shù)據(jù)類(lèi)型,為變量指明某種數(shù)據(jù)類(lèi)型后就不能賦予其他類(lèi)型的數(shù)據(jù)了,除非經(jīng)過(guò)強(qiáng)制類(lèi)型轉(zhuǎn)換或隱式類(lèi)型轉(zhuǎn)換。典型的強(qiáng)類(lèi)型語(yǔ)言有 C/C++、Java、C# 等。
int a = 123; //不轉(zhuǎn)換 a = 12.89; //隱式轉(zhuǎn)換 12(舍去小數(shù)部分) a = (int)"heiren,HelloWorld"; //強(qiáng)制轉(zhuǎn)換(得到字符串的地址) 不同類(lèi)型之間轉(zhuǎn)換需要強(qiáng)制
//Java 對(duì)類(lèi)型轉(zhuǎn)換的要求比 C/C++ 更為嚴(yán)格,隱式轉(zhuǎn)換只允許由低向高轉(zhuǎn),由高向低轉(zhuǎn)必須強(qiáng)制轉(zhuǎn)換。 int a = 100; //不轉(zhuǎn)換 a = (int)12.34; //強(qiáng)制轉(zhuǎn)換(直接舍去小數(shù)部分,得到12)
弱類(lèi)型語(yǔ)言-在定義變量時(shí)不需要顯式地指明數(shù)據(jù)類(lèi)型,編譯器(解釋器)會(huì)根據(jù)賦給變量的數(shù)據(jù)自動(dòng)推導(dǎo)出類(lèi)型,并且可以賦給變量不同類(lèi)型的數(shù)據(jù)。典型的弱類(lèi)型語(yǔ)言有 JavaScript、Python、PHP、Ruby、Shell、Perl 等。
var a = 100; //賦給整數(shù)
a = 12.34; //賦給小數(shù)
a = "heiren,HelloWorld"; //賦給字符串
a = new Array("JavaScript","React","JSON"); //賦給數(shù)組強(qiáng)類(lèi)型語(yǔ)言在編譯期間就能檢測(cè)某個(gè)變量的操作是否正確,因?yàn)樽兞康念?lèi)型始終哦都市確定的,加快了程序的運(yùn)行;對(duì)于弱類(lèi)型的語(yǔ)言,變量的類(lèi)型可以隨時(shí)改變,編譯器在編譯期間能確定變量的類(lèi)型,只有等到程序運(yùn)行后、賦值后才能確定變量當(dāng)前是什么類(lèi)型,所以傳統(tǒng)的編譯對(duì)弱類(lèi)型語(yǔ)言意義不大。
- 解釋型語(yǔ)言-弱類(lèi)型往往是解釋型語(yǔ)言,邊執(zhí)行邊編譯
- 編譯型語(yǔ)言-先編譯后執(zhí)行。
強(qiáng)類(lèi)型語(yǔ)言較為嚴(yán)謹(jǐn),在編譯時(shí)就能發(fā)現(xiàn)很多錯(cuò)誤,適合開(kāi)發(fā)大型的、系統(tǒng)級(jí)的、工業(yè)級(jí)的項(xiàng)目;而弱類(lèi)型語(yǔ)言較為靈活,編碼效率高,部署容易,學(xué)習(xí)成本低,在 Web 開(kāi)發(fā)中大顯身手。另外,強(qiáng)類(lèi)型語(yǔ)言的 IDE 一般都比較強(qiáng)大,代碼感知能力好,提示信息豐富;而弱類(lèi)型語(yǔ)言一般都是在編輯器中直接書(shū)寫(xiě)代碼。
C++模板退出的動(dòng)力來(lái)源是對(duì)數(shù)據(jù)結(jié)構(gòu)的封裝:數(shù)據(jù)結(jié)構(gòu)關(guān)注的是數(shù)據(jù)的存儲(chǔ)以及對(duì)其的增刪改查操作,C++開(kāi)發(fā)者們想封裝這些結(jié)構(gòu),但是這些結(jié)構(gòu)中數(shù)據(jù)成分的類(lèi)型無(wú)法提前預(yù)測(cè),于是模板誕生了。
STL(Standard Template Library,標(biāo)準(zhǔn)模板庫(kù))就是c++對(duì)數(shù)據(jù)結(jié)構(gòu)封裝后的稱(chēng)呼。
20、命名空間和異常處理
20.1 命名空間
命名空間實(shí)際上是由用戶(hù)自己命名的一塊內(nèi)存區(qū)域,用戶(hù)可以根據(jù)需要指定一個(gè)有名字的空間區(qū)域,每個(gè)命名空間都有一個(gè)作用域,將一些全局實(shí)體放在該命名空間中,就與其他全局實(shí)體分割開(kāi)來(lái)。
命名空間定義的一般形式:
namespace [命名空間名]//名省略時(shí),表示無(wú)名的命名空間
{
命名空間成員;
}命名空間成員的引用:命名空間名::命名空間成員名
使用命名空間別名:namespace 別名 = 命名空間名
使用using聲明命名空間成員的格式:using 命名空間名::命名空間成員名;
使用using聲明命名空間的全部成員:using namespace 命名空間名;
- using聲明后,在using語(yǔ)句所在的作用域中使用該命名空間成員時(shí),不必再用命名空間名加以限定。
- 標(biāo)準(zhǔn)C++庫(kù)的所有標(biāo)識(shí)符(包括函數(shù)、類(lèi)、對(duì)象和類(lèi)模板)都是在一個(gè)名為std的命名空間中定義的。
- 無(wú)名的命名空間,只在本文件的作用域內(nèi)有效。
20.2 異常處理
異常就是程序在執(zhí)行過(guò)程中,由于使用環(huán)境變化和用戶(hù)操作等原因產(chǎn)生的錯(cuò)誤,從而影響程序的運(yùn)行。
- 程序中常見(jiàn)的錯(cuò)誤:語(yǔ)法錯(cuò)誤,運(yùn)行錯(cuò)誤
- 異常處理機(jī)制的組成:檢查異常(try)、拋出異常(throw)、捕獲并處理異常(catch)
異常處理語(yǔ)句:
- 被檢查的語(yǔ)句必須放在try后面的{}中,否則不起作用,{}不能省略。
- 進(jìn)行異常處理的語(yǔ)句必須放在catch后面的{}中,catch后()中的異常信息類(lèi)型不能省略,變量名可以省略。
- catch語(yǔ)句塊不能單獨(dú)使用,必須和try語(yǔ)句塊作為整體出現(xiàn)。
- 在try-catch結(jié)構(gòu)中,只能有一個(gè)try,但可以有多個(gè)catch.
- catch(…)通常放在最后,可以捕獲任何類(lèi)型的異常信息。
try
{
被檢查的語(yǔ)句(可以有多個(gè)throw語(yǔ)句);
}
catch(異常信息類(lèi)型 [變量名])
{
}throw語(yǔ)句:thow 變量或表達(dá)式;
throw放在try中,也可以單獨(dú)使用,向上一層函數(shù)尋找try-catch,沒(méi)有找到系統(tǒng)就會(huì)調(diào)用系統(tǒng)函數(shù)terminate,使程序終止運(yùn)行。
try
{
throw 123;
}
catch (int a)
{
cout << a << endl;//123
}c++中沒(méi)有finally
類(lèi)的異常處理,當(dāng)在try中定義了類(lèi)對(duì)象,在try中拋出異常前創(chuàng)建的對(duì)象將被自動(dòng)釋放。
異常規(guī)范-描述了一個(gè)函數(shù)允許拋出那些異常類(lèi)型。
- 異常規(guī)范應(yīng)同時(shí)出現(xiàn)在函數(shù)聲明和函數(shù)定義中。
- 如果沒(méi)有異常規(guī)范,可以?huà)伋鋈魏晤?lèi)型的異常。
異常規(guī)范的一般形式:函數(shù)類(lèi)型 函數(shù)名(參數(shù)類(lèi)型)throw ([異常類(lèi)型1,異常類(lèi)型2,...])
float fun(float float)throw(int,float,double);
C++標(biāo)準(zhǔn)異常

| 異常 | 描述 |
|---|---|
| std::exception | 該異常是所有標(biāo)準(zhǔn) C++ 異常的父類(lèi)。 |
| std::bad_alloc | 該異??梢酝ㄟ^(guò) new 拋出。 |
| std::bad_cast | 該異??梢酝ㄟ^(guò) dynamic_cast 拋出。 |
| std::bad_exception | 這在處理 C++ 程序中無(wú)法預(yù)期的異常時(shí)非常有用。 |
| std::bad_typeid | 該異??梢酝ㄟ^(guò) typeid 拋出。 |
| std::logic_error | 理論上可以通過(guò)讀取代碼來(lái)檢測(cè)到的異常。 |
| std::domain_error | 當(dāng)使用了一個(gè)無(wú)效的數(shù)學(xué)域時(shí),會(huì)拋出該異常。 |
| std::invalid_argument | 當(dāng)使用了無(wú)效的參數(shù)時(shí),會(huì)拋出該異常。 |
| std::length_error | 當(dāng)創(chuàng)建了太長(zhǎng)的 std::string 時(shí),會(huì)拋出該異常。 |
| std::out_of_range | 該異??梢酝ㄟ^(guò)方法拋出,例如 std::vector 和 std::bitset<>::operator。 |
| std::runtime_error | 理論上不可以通過(guò)讀取代碼來(lái)檢測(cè)到的異常。 |
| std::overflow_error | 當(dāng)發(fā)生數(shù)學(xué)上溢時(shí),會(huì)拋出該異常。 |
| std::range_error | 當(dāng)嘗試存儲(chǔ)超出范圍的值時(shí),會(huì)拋出該異常。 |
| std::underflow_error | 當(dāng)發(fā)生數(shù)學(xué)下溢時(shí),會(huì)拋出該異常。 |
21、STL
C++標(biāo)準(zhǔn)模板庫(kù)(Standard Template Library,STL)是泛型程序設(shè)計(jì)最成功的實(shí)例。STL是一些常用數(shù)據(jù)結(jié)構(gòu)和算法的模板的集合,由Alex Stepanov主持開(kāi)發(fā),于1998年被加入C++標(biāo)準(zhǔn)。
C++ 標(biāo)準(zhǔn)模板庫(kù)的核心包括三大組件:容器,算法,迭代器
21.1 容器
順序容器:可變長(zhǎng)動(dòng)態(tài)數(shù)組Vector、雙端隊(duì)列deque、雙向鏈表list
關(guān)聯(lián)容器:set、multliset、map、multimap
關(guān)聯(lián)容器內(nèi)的元素是排序的,所以查找時(shí)具有非常好的性能。
容器適配起:棧stack、隊(duì)列queu、優(yōu)先隊(duì)列priority_queue
所有容器具有的函數(shù):
int size(); bool empty();
順序容器和關(guān)聯(lián)容器函數(shù):
begin() end() rbegin() erase(...) clear()
順序容器獨(dú)有的函數(shù):
front() back() push_back(); pop_back(); insert(...);
21.2 迭代器
迭代器是一種檢查容器內(nèi)元素并遍歷元素的數(shù)據(jù)類(lèi)型。C++更趨向于使用迭代器而不是下標(biāo)操作,因?yàn)闃?biāo)準(zhǔn)庫(kù)為每一種標(biāo)準(zhǔn)容器(如vector)定義了一種迭代器類(lèi)型,而只用少數(shù)容器(如vector)支持下標(biāo)操作訪問(wèn)容器元素。按照定義方式分為以下四種。
1.正向迭代器:容器類(lèi)名::iterator 迭代器名;
2.常量正向迭代器:容器類(lèi)名::const_iterator 迭代器名;
3.反向迭代器:容器類(lèi)名::reverse_iterator 迭代器名;
4.常量反向迭代器:容器類(lèi)名::const_reverse_iterator 迭代器名
21.3 算法
STL 提供能在各種容器中通用的算法(大約有70種),如插入、刪除、查找、排序等。算法就是函數(shù)模板。算法通過(guò)迭代器來(lái)操縱容器中的元素。
STL 中的大部分常用算法都在頭文件 algorithm 中定義。此外,頭文件 numeric 中也有一些算法。
許多算法操作的是容器上的一個(gè)區(qū)間(也可以是整個(gè)容器),因此需要兩個(gè)參數(shù),一個(gè)是區(qū)間起點(diǎn)元素的迭代器,另一個(gè)是區(qū)間終點(diǎn)元素的后面一個(gè)元素的迭代器。
會(huì)改變其所作用的容器。例如:
- copy:將一個(gè)容器的內(nèi)容復(fù)制到另一個(gè)容器。
- remove:在容器中刪除一個(gè)元素。
- random_shuffle:隨機(jī)打亂容器中的元素。
- fill:用某個(gè)值填充容器。
不會(huì)改變其所作用的容器。例如:
- find:在容器中查找元素。
- count_if:統(tǒng)計(jì)容器中符合某種條件的元素的個(gè)數(shù)。
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4); //1,2,3,4
vector<int>::iterator p;
p = find(v.begin(), v.end(), 3); //在v中查找3 若找不到,find返回 v.end()
if (p != v.end())
cout << "1) " << *p << endl; //找到了
p = find(v.begin(), v.end(), 9);
if (p == v.end())
cout << "not found " << endl; //沒(méi)找到
p = find(v.begin() + 1, v.end() - 1, 4); //在2,3 這兩個(gè)元素中查找4
cout << "2) " << *p << endl; //沒(méi)找到,指向下一個(gè)元素4
int arr[10] = { 10,20,30,40 };
int * pp = find(arr, arr + 4, 20);
if (pp == arr + 4)
cout << "not found" << endl;
else
cout << "3) " << *pp << endl;
return 0;
}22、總結(jié)
終于寫(xiě)完了,因?yàn)樾鹿谝咔?,在家隔離了一個(gè)多月,閑來(lái)無(wú)事,寫(xiě)寫(xiě)博客來(lái)總結(jié)一下自己之前學(xué)的知識(shí);零零散散大約花了一周左右的時(shí)間,該篇文章面向C++的基礎(chǔ)知識(shí),有許多詳細(xì),深層的沒(méi)有寫(xiě)進(jìn)去。后面有時(shí)間會(huì)寫(xiě)模板,STL,C++11新特性,Boost庫(kù)等的文章,剛開(kāi)始寫(xiě)博客,文章中有錯(cuò)誤或有遺漏的知識(shí)點(diǎn),請(qǐng)大佬們指出。
相關(guān)文章
C語(yǔ)言?推理證明帶環(huán)鏈表詳細(xì)過(guò)程
單鏈表中同樣也有具有挑戰(zhàn)性的題目,鏈表的帶環(huán)問(wèn)題可以說(shuō)是眾多難題中的佼佼者,在這里可能更看重的是邏輯推理和證明的過(guò)程2022-04-04
C語(yǔ)言嵌套鏈表實(shí)現(xiàn)學(xué)生成績(jī)管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言嵌套鏈表實(shí)現(xiàn)學(xué)生成績(jī)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07
C++用函數(shù)對(duì)算法性能進(jìn)行測(cè)試
算法無(wú)處不在,算法是程序的靈魂,而數(shù)據(jù)結(jié)構(gòu)則是程序的骨架,二者共同構(gòu)成了程序,那么如何評(píng)估算法的性能呢?理論上可以通過(guò)計(jì)算時(shí)間復(fù)雜度的方法來(lái)評(píng)估,但這是理性的認(rèn)識(shí),我們還有一種直觀的評(píng)估方法,那就是程序執(zhí)行的時(shí)間2022-08-08

