C++17入門經(jīng)典上
Chapter 1 基本概念
- 頭文件包含許多內(nèi)容呼巴,其中包括.cpp文件中的可執(zhí)行代碼使用的函數(shù)原型,以及使用的類和模板的定義
- 預(yù)處理指令會(huì)以某種方式修改源代碼盈滴,之后會(huì)把他們編譯為可執(zhí)行的形式
- 頭文件的內(nèi)容會(huì)添加到源文件中
- 頭文件的內(nèi)容被插入到#include指令的位置
- 流是數(shù)據(jù)源或數(shù)據(jù)接收器的一種抽象表示账劲,每個(gè)流都關(guān)聯(lián)著某臺(tái)設(shè)備奕短,關(guān)聯(lián)著數(shù)據(jù)源的流就是輸入流婉刀,關(guān)聯(lián)著目的地的流就是輸出流
- 名稱空間類似于姓氏
- 兩個(gè)冒號(hào)::有一個(gè)非常奇特的名稱帜平,作用域解析運(yùn)算符
- main函數(shù)不能定義在名稱空間中幽告,未在名稱空間中定義的內(nèi)容都存在于全局名稱空間中,全局名稱空間沒有名稱
- 不要使用以下劃線開頭的名稱
- 有時(shí)候程序需要幾個(gè)類似的類或函數(shù)裆甩,其代碼中只有所處理的數(shù)據(jù)類型有區(qū)別冗锁,編譯器使用模板給特定的自定義類型自動(dòng)生成類或函數(shù)代碼
Chapter 2 基本數(shù)據(jù)類型
- 花括號(hào)稱為初始化列表,初始化列表可以包含幾個(gè)值嗤栓;優(yōu)勢(shì):允許以相同的方式初始化所有變量冻河,被稱為統(tǒng)一初始化
- 可以在花括號(hào)中使用單個(gè)值來初始化任何變量 c++17
int apple_count{12};
int counter{};//零初始化 {}相當(dāng)于0
const unsigned toe_count{10};//常量
- const 可以固定任何類型的變量值箍邮,不可被修改
- unsigned 是無符號(hào)數(shù),永遠(yuǎn)不會(huì)是負(fù)值
- 整數(shù)除法返回的是分母除以分子得到的倍數(shù)叨叙,任何余數(shù)都會(huì)被舍棄
- 浮點(diǎn)類型數(shù)據(jù)不能使用unsigned或signed修飾符锭弊,浮點(diǎn)類型總是帶符號(hào)的
- cmath頭文件中的所有函數(shù)可接受任意浮點(diǎn)類型或整型參數(shù)
- abs(arg)
- ceil(arg)
- floor(arg)
- exp(arg)
- sqrt(arg)
- round(arg)
- pow(arg1,arg2)
- tan(angle)
- iomanip頭文件,控制數(shù)據(jù)格式
- fixed 用小數(shù)點(diǎn)固定格式
- dec 十進(jìn)制格式
- left 左對(duì)齊格式
- setprecision(n) 一共n位輸出擂错,if有fixed味滞,則表示小數(shù)點(diǎn)后n位數(shù)字
- setw(n) 輸出序列寬度
- setfill(ch) ch填充多余,默認(rèn)是空格
- static_cast<要轉(zhuǎn)的新類型>(數(shù)據(jù)) 強(qiáng)制類型轉(zhuǎn)換
- auto 關(guān)鍵字可以告訴編譯器應(yīng)推斷數(shù)據(jù)類型
auto num{10};
Chapter 3 處理基本數(shù)據(jù)類型
- 枚舉 定義:
enum class 類名:指定成員的類型{枚舉成員1,...,枚舉成員n};//默認(rèn)枚舉成員1 = 0钮呀,后一個(gè)比前一個(gè)大1
// 成員類型可以省略不寫
可以當(dāng)成類來看待剑鞍,就是一個(gè)類名數(shù)據(jù)類型,引用枚舉時(shí)行楞,必須使用類型名(類名)來限定他
枚舉成員的值必須是編譯器可以計(jì)算出來的常量表達(dá)式攒暇,這種表達(dá)式包括字面量,以前定義的枚舉成員子房,聲明為const的變量形用,不能使用非const變量(即使使用字面量初始化也不行)
enum class Punctuation : char{Comma=',',Exclamation='!',Question='?'};
- using關(guān)鍵字允許把自己的數(shù)據(jù)類型名稱指定為另一個(gè)類型的替代名稱
using BigOnes = unsigned long long;
- 全局變量的初始化在main()開始之前進(jìn)行,默認(rèn)情況下被初始化為0.局部變量將全局變量隱藏時(shí)证杭,使用作用域解析運(yùn)算符(::)來限定它
Chapter 4 決策
-
cctype頭文件
- isupper(c) c是否是大寫字母
- isalpha(c) c是否是字母
- isdigit(c) c是否是數(shù)字
- isspace(c) c是否是空白田度,\n \t \r \f ' '
- isblank(c) c是否是空格 ‘ ’ \t‘
- tolower(c) 返回c的小寫
- isalnum(c) if c 包含字母或數(shù)字,就返回一個(gè)正整數(shù)(true)else 0(false)
if(變量){} == if(變量!=0) == if(變量!=false) //內(nèi)解愤! 外==
if(!變量){} == if(變量==0) == if(變量==false)
Chapter 5 數(shù)組和循環(huán)
數(shù)組
- 數(shù)組的大小必須用常量表達(dá)式來指定
- 數(shù)組沒有初始化镇饺,所以包含的都是垃圾值
double temp[100];
temp[3] = 99.0;
unsigned int height[6];
unsigned int height1[6]{1,2,3,4,5,6};
int height3[6] {};// all 0
const int height4[6]{1,2,3,4,5,6};
- size_t 是某個(gè)不帶符號(hào)的整數(shù)類型的別名,足夠大送讲,能夠容納編譯器支持的任何類型(包括數(shù)組)
- 幻數(shù):產(chǎn)量值的多次使用直接定義為const
- 數(shù)組大小一般設(shè)置為unsigned奸笤,一般不會(huì)為負(fù)值
- std::size(array); 獲取數(shù)組的大小,c++17
- c++可以使用浮點(diǎn)數(shù)來控制for循環(huán)
//無限循環(huán)中哼鬓,輸入指定個(gè)數(shù)元素
x[count] = input;
if(++count == size){
cout<<"無法容納"<<size<<"個(gè)元素"<<endl;
break;
}
- 無符號(hào)數(shù)減去值時(shí)候應(yīng)該小心监右,0-1=numice_limits<size>::max()
- 字符數(shù)組初始化字符串字面量,默認(rèn)會(huì)加上 ’\0‘
char name{"aeiou"};
//6 elements
//因?yàn)樽址淖詈筇砑恿?\0'來標(biāo)記字符串結(jié)束异希,所以數(shù)組包含6個(gè)元素
- 使用數(shù)組名不能輸出數(shù)值類型的數(shù)組的內(nèi)容健盒,這種方法僅僅用于輸出char數(shù)組,即使是傳送給輸出流的char數(shù)組称簿,也必須用空字符結(jié)束扣癣,否則程序很可能崩潰
const int maxLength{100};
char text[maxLength]{}
for(int i{};text[i]!='\0';i++){//一個(gè)字符數(shù)組中的原因個(gè)數(shù)
if(isalpha(text[i])){
switch(tolower(text[i])){
case 'a':case 'e':case 'i': case 'o' :case 'u':
++vowels;
break;
default:
++consonants;
}
}
}
- 不能使用cin>>直接讀取輸入內(nèi)容,因?yàn)?gt;>不能碰到空格
- cin.getline(text,maxlength);
- 多維數(shù)組和一維的類似憨降,空初始化列表是將其初始化為0
- 多維數(shù)組除了第一個(gè)緯度之外父虑,必須指定大小,編譯器只能推斷第一個(gè)緯度的大小
- 在運(yùn)行期間給數(shù)組分配內(nèi)存空間
cin>>count;
unsigned int height[count];
//此時(shí)數(shù)組無法初始化授药,因?yàn)椴恢烙卸嗌僭亍?//因此這種帶有變量個(gè)數(shù)的數(shù)組频轿,只能先聲明,后賦值,不可直接初始化
數(shù)組的替代品(優(yōu)于數(shù)組)
std::array
array<T,N>
include<array>
- if 創(chuàng)建array<>容器但不指定初始值赚窃,則數(shù)組也包含垃圾值
- fill()函數(shù)把所有元素設(shè)置為某個(gè)給定的值
array<double,100> values{};
values.fill(3.1415);// all elements to pi
- array.size(); 返回?cái)?shù)組的元素個(gè)數(shù)size_t類型键痛,array對(duì)象總是可以通過size()函數(shù)確定自己的大小
- array.at(i); 返回i出的值昨忆,i是一個(gè)索引
- array.front();返回?cái)?shù)組第一個(gè)值
- array.back();返回?cái)?shù)組最后一個(gè)值
- 兩個(gè)array只要容器大小相同拢驾,存儲(chǔ)類型相同嵌洼,可以使用== != > < 對(duì)兩個(gè)array中的元素進(jìn)行逐個(gè)比對(duì)鳖昌,也可相互賦值
std::vector<T>
- 大小可自動(dòng)增加糕档,可容納任意數(shù)量元素
- push_back(X);向vector中添加元素
vector<long> numbers(20,99L);//理解為構(gòu)造器()捶惜,20個(gè) 99
vector<int> number(20);//有20個(gè)0
vector<long> number{20};//只有一個(gè)20
vector<unsigned int> primes{1,2,3,4,4,4};//初始化{}列表
- 與array類似陪捷,兩個(gè)vector也可以比較苍碟,但是vector可以元素?cái)?shù)量不同的比蜓肆,字典序比較
- 一個(gè)vector給另外一個(gè)vector賦值時(shí),被賦值的會(huì)被覆蓋已經(jīng)存在的值
- vector可以存儲(chǔ)在其他容器中
- vector沒有fill()成員,但是提供了assign函數(shù)黎烈,可用于重新初始化vector<>內(nèi)容
vector<long> numbers(20,99L);//20個(gè)99
numbers.assign(99,20L);//99個(gè)20
numbers.assign({99L,20L});//99 和 20
numbers.clear();//清空numbers
numbers.empty()//vector中是空趴捅,沒有數(shù)據(jù)嗎?返回true orfalse
numbers.pop_back();//刪除vector最后一個(gè)元素
如果編譯時(shí)知道元素的準(zhǔn)確數(shù)量霹疫,就是用array<>,如果不知道就是用vector<>
Chapter 6 指針和引用
指針
- 指針是可存儲(chǔ)地址的變量拱绑。他指向內(nèi)存中存儲(chǔ)了其他值的位置
- 初始化列表為空,所以這個(gè)語句把pnumber初始化為等價(jià)于0的指針丽蝎,即不指向任何內(nèi)容的地址猎拨,等價(jià)于0的指針寫為nullptr
long* pnumber{nullptr};
long *pnumber{nullptr};
- 定義指針時(shí),總是要初始化他屠阻,如果還不能為指針提供期望的值红省,就將其初始化為nullptr
- 最好在單獨(dú)的代碼行上聲明指針和普通變量,以避免出現(xiàn)這種混淆国觉。
- 不管指針指向什么類型或大小的數(shù)據(jù)吧恃,指針變量本身的大小始終是相同的,如今的平臺(tái)上指針變量大小一般是4或8字節(jié)
地址運(yùn)算符&
- &是一個(gè)一元運(yùn)算符麻诀,他可以獲取變量的地址痕寓。
- &運(yùn)算符可以應(yīng)用于任何類型的變量,但必須在對(duì)應(yīng)類型的指針中存儲(chǔ)地址
- 使用編譯器推斷指針類型蝇闭,但是盡量還是使用auto*
auto* number{&height};
- 使用auto*聲明的變量只能用指針值初始化呻率,使用其他類型的值初始化,會(huì)導(dǎo)致編譯錯(cuò)誤丁眼。
間接運(yùn)算符
- 將間接運(yùn)算符*應(yīng)用于指針筷凤,可以訪問指針?biāo)赶虻膬?nèi)存位置的數(shù)據(jù)昭殉,也成為為解引用運(yùn)算符
int count{};
int* pount{&count};
- *是乘法運(yùn)算符苞七,間接運(yùn)算符藐守,還可以應(yīng)用于聲明指針,一般編譯器根據(jù)上下文分析
- 使用指針的用處
- 動(dòng)態(tài)的為新變量分配內(nèi)存空間蹂风,即可以在程序執(zhí)行過程中分配
- 使用指針表示法操作存儲(chǔ)在數(shù)組中的數(shù)據(jù)卢厂,與普通數(shù)組表示法完全等效
- 指針可以在函數(shù)中訪問函數(shù)外部定義的大塊數(shù)據(jù)
- 指針是支持多態(tài)性起作用的基礎(chǔ)
char類型的指針
- 指向char類型的變量,可以用字符串字面量(實(shí)際上是const char類型的數(shù)組)初始化
- 給未解除引用的char類型指針應(yīng)用<<插入運(yùn)算符-----假定這種指針包含以空字符結(jié)尾的字符串的地址
- 如果給解除引用的char類型指針應(yīng)用<<插入運(yùn)算符---將地址中的單個(gè)字符寫入cout
const char* pproverb{"A miss is as good as a mail"};
void proj1()
{
const char* pstr1{ "fatty arb" };
const char* pstr2{ "clara bow" };
const char* pstr3{ "lassie" };
const char* pstr{ "your lucky star is " };
cout << "pick a lucky star! enter a number between 1 and 3:" << endl;
size_t choice{};
cin >> choice;
switch (choice)
{
case 1:cout << pstr << pstr1 << endl; break;//your lucky star is fatty arb
case 2:cout << pstr << pstr2 << endl; break;//字符串名字直接輸出字符串
case 3:cout << pstr << pstr3 << endl; break;//未解除引用
default:cout << "sorry you haven't got a lucky star." << endl;
}
cout << *pstr1 << endl;//輸出首字符(首地址) 解除引用
}
- 指針數(shù)組
每個(gè)因子都是一個(gè)const char* 惠啄,一個(gè)const char* 指向一個(gè)字符串字面量
其中的*pstr[i] 無法進(jìn)行再賦值慎恒,因?yàn)槠錇槌A縞onst,無法進(jìn)行修改
void proj2()
{
const char* pstars[] = {//默認(rèn)都會(huì)加上\0
"fatty","clara","lassie","slim","boris","mae","oliver","greta"
};
//pstars[]數(shù)組中的每個(gè)元素 會(huì)指向一個(gè)const char類型的變量(串/字符數(shù)組)
cout << "pick a lucky star! enter a number between 1 and " << size(pstars) << endl;
int choice{};
cin >> choice;
if (choice>0 &&choice<size(pstars))
{
cout << "your lucky star is " << pstars[choice - 1] << endl;
}
else
{
cout << "sorry!" << endl;
}
pstars[0] = pstars[1];//數(shù)組內(nèi)部可以相互賦值撵渡,就很離譜H诩怼!
}//指針數(shù)組法
把一個(gè)聲明從右向左讀趋距。
char * const cp; ( * 讀成 pointer to指向 )
cp is a const pointer to char
const char * p;
p is a pointer to const char;
const char* my_favorite_star{ "Lassie" };
my_favorite_star = "Mae";//my_favorite_star本身不是const變量粒氧, my_favorite_star = pstars[1];
- 指向常量的指針
指針指向的內(nèi)容不能修改,但可以把指針設(shè)置為指向其他內(nèi)容
const char* pstring{"some text that cannot be changed"};
const int value{20};
const int* pvalue{&value};
int a{ 1 };
pvalue = &a;
value是一個(gè)常量节腐,不能修改外盯,pvalue是一個(gè)指向常量的指針,可以用于存儲(chǔ)value的地址翼雀。不能在非const int指針中存儲(chǔ)value的地址饱苟。
但是可以把非const變量賦給pvalue、
- 常量指針
存儲(chǔ)在指針中的地址不能修改狼渊。
只能指向初始化時(shí)指定的地址箱熬。
但是地址的內(nèi)容不是常量,可以修改囤锉,
int data{ 20 };
int* const pdata{ &data };
*pdata = 25;//內(nèi)容可變
- 指向常量的常量指針
因?yàn)榇鎯?chǔ)在指針中的地址和指針指向的內(nèi)容都被聲明為常量坦弟,所以兩者都不能修改
const float value{3.14};
const float* const pvalue{&value};
指針和數(shù)組
- 數(shù)組名可以像指針那樣操作,輸出時(shí)官地,使用非char類型的數(shù)組名酿傍,就可以得到內(nèi)存中的地址。
int num[]{ 1,2,3 };
cout << num << endl;
-
指針可進(jìn)行的運(yùn)算:
-
加減:(在數(shù)組中表現(xiàn)為向后/前移動(dòng)一位驱入,就是一個(gè)類型的字節(jié)數(shù))
-
指針+整數(shù)=指針
int data[]{1,2,4}; int* pdata{&data[1]}; cout<<* (pdata+1)<<endl;//4
指針-整數(shù)=指針
指針+-指針=整數(shù)(類型相同赤炒,同一數(shù)組中)
結(jié)果是兩個(gè)索引的差值
-
比較
-
動(dòng)態(tài)內(nèi)存分配
- 所有指針都應(yīng)該初始化,如果指針沒有包含合法的地址亏较,就應(yīng)該總讓他包含nullptr
double* pvalue{new double{}};//初始化為0.0
double* pnull{};//初始化為nullptr
delete pvalue;
pvalue = nullptr;
delete 只是將自由存儲(chǔ)區(qū)的內(nèi)存釋放莺褒,變量還可以使用,但是最好先將其置為nullptr
與普通數(shù)組不同的是雪情,無法讓編譯器推斷出動(dòng)態(tài)分配數(shù)組的維數(shù)
double* data{new double[100]};//100個(gè)垃圾值
int* num{new int[100]{0}};//100個(gè)0
int* one{new int[]{1,2,3}};//報(bào)錯(cuò)遵岩,無法推斷
delete[] data;//釋放動(dòng)態(tài)數(shù)組的內(nèi)存
data=nullptr;
- 釋放動(dòng)態(tài)數(shù)組的內(nèi)存,使用delete[] 或者delete [] 方括號(hào)表示刪除的數(shù)組
- ->指針選擇成員
- 多維動(dòng)態(tài)數(shù)組?
void proj6()
{
int rows{ 3 };
int columns{ 3 };
//動(dòng)態(tài)二維數(shù)組
//carrots數(shù)組是double*指針的一個(gè)動(dòng)態(tài)數(shù)組尘执,每個(gè)double*指針包含一個(gè)double數(shù)組的地址
double** carrots{ new double* [rows]{} };
for (size_t i = 0; i < rows; i++)
{
carrots[i] = new double[columns] {};
}
for (size_t i = 0; i < rows; i++)
{
delete[] carrots[i];
}
delete[] carrots;
}
- 每個(gè)new必須對(duì)應(yīng)一個(gè)delete舍哄,每個(gè)new[] 必須對(duì)應(yīng)一個(gè)delete[]
- 在c++程序設(shè)計(jì)中,盡量不要使用new delete new[] delete[]誊锭,應(yīng)該使用vector<> 和智能指針來規(guī)避動(dòng)態(tài)內(nèi)存的一些風(fēng)險(xiǎn)表悬。盡量避免直接操作動(dòng)態(tài)內(nèi)存。
智能指針
不必使用delete delete[] 運(yùn)算符釋放內(nèi)存
智能指針不能進(jìn)行遞增或遞減丧靡,也不能進(jìn)行算術(shù)操作
<memory>頭文件
-
三種智能指針
-
unique_ptr<T>
- 這個(gè)對(duì)象類似于指向T類型的指針蟆沫,是惟一的。
- 從不會(huì)有兩個(gè)unique_ptr<>對(duì)象指向同一地址
- 指向的值被該對(duì)象獨(dú)占
unique_ptr<double> pdata{ new double{999.0} }; cout << pdata << endl;//地址 cout << *pdata << endl;//數(shù)值 cout << pdata.get() << endl;//get()函數(shù)返回地址 unique_ptr<double> pdata1{ make_unique<double>(999.0) }; auto pdata2{ make_unique<double>(1.9) };// //三個(gè)方法相同温治,推薦最后一種寫法饭庞,簡潔,且可以防止內(nèi)存泄漏
reset() 將指針重置為nullptr熬荆,如果參數(shù)不設(shè)置值但绕,就是nullptr
-
release() 將智能指針轉(zhuǎn)換為普通指針,注意惶看,將其轉(zhuǎn)化為原始指針之前一定要先保存原始指針再進(jìn)行釋放捏顺,否則將出現(xiàn)內(nèi)存泄漏的情況
const size_t n{ 100 }; unique_ptr<double[]> pvalues{ new double[n] }; auto pvalues1{ new double[n] }; auto pvalues2{ make_unique<double[]>(n) };//動(dòng)態(tài)創(chuàng)建n個(gè)元素的數(shù)組 cout << pvalues1 << endl;//地址 for (size_t i = 0; i < n; i++) { pvalues2[i] = i + 1; } /*for (size_t i = 0; i < n; i++) { cout << pvalues2[i] << endl; }*/ pvalues.reset();//將指針重置為nullptr double* values = pvalues2.release(); delete values;
-
-
shared_ptr<T>
-
創(chuàng)建shared_ptr<T> 過程更復(fù)雜一些,主要是需要維護(hù)引用計(jì)數(shù)纬黎,
void proj8() { shared_ptr<double> pdata{ new double{111.1} }; shared_ptr<double> pdata2;//初始化為nullptr pdata2 = pdata; cout << *pdata2 << endl; }
- 復(fù)制pdata會(huì)增加引用次數(shù)幅骄,兩個(gè)指針必須重置或釋放,double變量占用的內(nèi)存才會(huì)釋放
- 實(shí)際使用共享指針的情況通常涉及對(duì)象
-
weak_ptr<T>
理解引用
- 引用就是一個(gè)別名本今,可以用作某對(duì)象的別名拆座,不可以只聲明引用而不對(duì)其初始化
- 引用不能修改另一個(gè)另一個(gè)對(duì)象的別名
- 類型名后面&符號(hào)表示引用,如果取得引用的地址冠息,結(jié)果會(huì)是指向原始變量的一個(gè)指針
就當(dāng)做別名去理解挪凑,想不清楚就用原名
void proj9()
{
double data{ 3.5 };
double& rdata{ data };
double* pdata1{ &rdata };
double* pdata2{ &data };
cout << (pdata1 == pdata2) << endl;//相等
double* pdata{ &data };
*pdata += 2.5;
double other_data{ 5.0 };
rdata = other_data;
cout << rdata << endl;
}
Chapter 7 操作字符串
-
c++的string頭文件定義了string類型,相比ctring頭文件中以‘\0’結(jié)尾的c字符串更可靠
-
定義string對(duì)象的六種方式
- empty
- 字面量
- 字面量切割
- 構(gòu)造器
- string對(duì)象
- string對(duì)象切割
string empty;//長度為0逛艰,不包含字符的字符串empty
string proverb{"many a mickle make a muckle."};//字面量
string part_literal{"least said soonest mended.",5};//least 字面量切割
string sleeping(6,'a');//6個(gè)a 構(gòu)造器
string sentence{proverb}; //string 對(duì)象
string sentence{proverb,0,13};//(begin,num) //string對(duì)象的切割
-
c串與string串的轉(zhuǎn)化躏碳,(string 字符串的長度不會(huì)計(jì)算\0,但是有\(zhòng)0存在(不理它))
- string.c_str();
- string.data();
string proverb{"many a mickle make a muckle."};//字面量
const char* proverb_c_str = proverb.c_str();//const char* 類型字符串
//const char* 表示里面的字符是不能變化的散怖,但是proverb_c_str可以變菇绵,指別的地方
char* prover_data = proverb.data();//不是const
cout<<proverb.length();
-
string對(duì)象的操作
- 賦值
- 字面量賦值
- 串變量賦值
string adj{"hornwogging"};//字面量賦值 string word{"ribbish"}; word{adj};//串變量賦值 adj = "twotiming";
- 連接
- +連接:必須有一個(gè)string對(duì)象在+的一側(cè)
- string.append(str,begin,end);連接
- string.append(str,'字符');
string word{"this is a string object"} string description{"whipppersnapper"+word}; string compliment{"~~~what a beautiful name...~~~"}; sentence.append(complient,3,22);//what a beautiful time sentence.append(,'!');//!!!
- 數(shù)串連接:
? to_string(數(shù)字)+字符串
double num{ 100.0 }; string name{ "I'm Huang Hongwei.I am " }; cout << name + to_string(num) + " years old" << endl;
- 賦值
-
讀入串getline(cin,text)
string text{}; getline(cin, text); cout << text << endl;
-
訪問子串
str.substr(be,num)
str.substr(be)
str.substr()//父子相同
-
out_of_range類型
string phrase{ "The higher the fewer." }; string word1{ phrase.substr(4,6) }; cout << word1 << endl; string word2{ phrase.substr(4,100) }; cout << word2 << endl; string word{ phrase.substr(4) }; cout << word << endl; string str{ phrase.substr() }; cout << str << endl;
-
比較字符串
-
字典序比較:
- 前面的字符都相同镇眷,看長度咬最,越長越大
- 長度相同,且對(duì)應(yīng)字符相同欠动,則相同
string對(duì)象可以存儲(chǔ)到容器中永乌,普通的char數(shù)組不能存儲(chǔ)到容器中。std空間定義了一個(gè)非成員函數(shù)模板,實(shí)現(xiàn)效果與swap(a翅雏,b)相同
-
compare()函數(shù)
-
obj. compare()函數(shù)可以比較該對(duì)象
for(size_t i{1};i<names.size();++i) { if(names[i-1].compare(names[i]>0)) { names[i].swap(names[i-1]); sorted = false; } }
-
使用substr()進(jìn)行比較
string text{ "peter piper picked a peck of pickled pepper. " }; string phrase{ "got to pick a pocket or two." }; for (size_t i = 0; i < text.length()-3; i++) { if (text.substr(i,4)==phrase.substr(7,4)) { cout << "text contains " << phrase.substr(7, 4) << " starting at index " << i << endl; } }
?
-
-
搜索字符串find()
string sentence{ "manners maketh man" };
string word{ "man" };
cout << sentence.find(word) << endl;
cout << sentence.find("ma")<< endl;
cout << sentence.find("k")<< endl;
cout << sentence.find("x")<< endl;//返回string:npos
- find(串硝桩,對(duì)象的初始查找點(diǎn))
cout<< sentence.find("an",1);//從1開始查找,結(jié)果是1
cout<<sentence.finde("an",3);//從3開始查找枚荣,結(jié)果是16,第二次出現(xiàn)an的位置
-
搜索任意字符集合find_first_of() find_last_of()
? 從頭開始 從結(jié)尾開始
string text{"hjkld,ddddd ddddd ddd ddd \"}; string operators{",.\""}; cout<<text.find_first_of(operators)<<endl;//返回集合中(有一個(gè)匹配就返回)第一個(gè)出現(xiàn)的位置 5
-
find_first_not_of() find_last_not_off()
搜索不在字符集合中的字符的位置
cout<<text.find_first_not_of("aeiouAEIOU");//查找第一個(gè)不是元音的位置
逆向搜索字符串str.rfind();
string sentence{"manners maketh man"};
string word{"an"};
cout<<sentence.rfind(word);//16 從后向前搜索啼肩,index=16是an第一次出現(xiàn)的地方
//從n開始搜索
-
修改字符串
-
插入 :索引位置前插入橄妆,充當(dāng)當(dāng)前索引insert()
string phrase{ "we can insert a string." }; string words{ "a string into " }; //phrase.insert(14, words);//在14位置插入words cout << phrase << endl; phrase.insert(13, words, 8,5); //words中從8開始的5個(gè)字符 插入到對(duì)象的13位置 cout << phrase << endl;
-
-
替換
string text{ "we can replace a string" }; text.replace(1, 5, "123456");//在index=1的地方,的5 個(gè)字符夏志,替換為123456 cout << text << endl;
-
刪除
刪除[begin,]之后的len個(gè)字符
unsigned begin{ text.find('c') };
int len{ 3 };
cout << text.erase(begin,len) << endl;
將字符串轉(zhuǎn)化為整型
stoi(string)
string s{"12334"};
int i{stoi(s)};
Chapter 8 定義函數(shù)
返回類型 函數(shù)名 (參數(shù)列表)
{
}
- 函數(shù)調(diào)用中的實(shí)參順序必須對(duì)應(yīng)于函數(shù)列表里的參數(shù)順序研侣。函數(shù)名和參數(shù)列表的組合稱為函數(shù)的簽名俘闯。
- 函數(shù)體中可以有多個(gè)return語句,每個(gè)return語句可能返回不同的值慌随。
- 返回類型是void->return ;
- 函數(shù)原型=函數(shù)聲明:定義了函數(shù)名,函數(shù)的返回值躺同,參數(shù)列表阁猜;
- 聲明一定要放在調(diào)用之前。蹋艺。除非把實(shí)現(xiàn)寫在寫在引用之前剃袍。(同c語言)
給函數(shù)傳遞實(shí)參
如果指定的函數(shù)實(shí)參類型不對(duì)應(yīng)參數(shù)類型,編譯器就會(huì)把參數(shù)的類型隱式轉(zhuǎn)換為參數(shù)類型
按值傳送:
實(shí)參的變量值或常量值根本不會(huì)傳送給函數(shù)捎谨,而是創(chuàng)建實(shí)參的副本民效,把這些副本傳給函數(shù)。執(zhí)行完函數(shù)之后就廢棄副本(不會(huì)對(duì)原有的值進(jìn)行更改)
- 給函數(shù)傳遞指針:參數(shù)為指針類型時(shí)涛救,按值傳送機(jī)制就會(huì)像以前那樣運(yùn)行畏邢,但是指針包含另一個(gè)變量的地址,此時(shí)检吆,指針的副本也包含這樣一個(gè)副本
#include<iostream>
#include<string>
#include<iomanip>
using namespace std;
double changeIt(double* pointer_to_it);
int main()
{
double it{ 15.0 };
double result{ changeIt(&it) };
cout << "After function execution, it = " << it << "\n Result returned is " << result << endl;
return 0;
}
double changeIt(double* pit)
{
*pit += 10.0;
cout << "within function, *pit = " << *pit << endl;
return *pit;
}
給函數(shù)傳送數(shù)組:
給函數(shù)傳送數(shù)組的地址要比傳送數(shù)組更高效舒萎,(不需要復(fù)制許多元素),函數(shù)體中的代碼可以把表示數(shù)組的參數(shù)作為指針來看待蹭沛,即函數(shù)體中可以給數(shù)組參數(shù)使用指針表示法的強(qiáng)大功能
void proj2()
{
double values[]{ 1.0,2.0,3.00,4.0,5.0,6.0,7.0,8.0,9.0,10.0 };
cout << "average = " << average(values, size(values)) << endl;
}
double average(double array[], size_t count)
{
double sum{};
for (size_t i = 0; i < count; i++)
{
sum += array[i];
}
return sum / count;
}
注意事項(xiàng):
不能通過size()來避免指定count值逆甜,數(shù)組參數(shù)array只是存儲(chǔ)數(shù)組的地址,并不是數(shù)組本身致板;
如果使用sizeof(array) 將返回?cái)?shù)組地址的內(nèi)存位置的大小交煞,而不是整個(gè)數(shù)組的大小
可以相互使用數(shù)組和指針表示法
double average2(double* array,size_t count)
{
double sum{};
for (size_t i = 0; i < count; i++)
{
sum += *array++; // 效果一樣
//sum+= array[i];
}
return sum / count;
}
存在誤區(qū):不要向函數(shù)傳遞固定大小的數(shù)組
如果只想計(jì)算10個(gè)數(shù)的平均值,
double average10(double array[10])
{
double sum{};
for (size_t i = 0; i < 10; i++)
{
sum += array[i];
}
return sum / 10;
}
這個(gè)函數(shù)的簽名啊雖然合法斟或,但是呢素征,會(huì)給人一種錯(cuò)誤的期待
編譯器會(huì)強(qiáng)制將剛好包含10個(gè)元素的數(shù)組作為實(shí)參傳遞給該函數(shù)
double average10(double array[10])
double average10(double array[])
double average10(double* array)
//等價(jià)的三種方式
//指定維數(shù)的數(shù)組如果本例中只輸入一個(gè)帶有三個(gè)元素的數(shù)組,那么仍然會(huì)使用10個(gè)去計(jì)算,下標(biāo)會(huì)越界
const 指針參數(shù)
只需要訪問數(shù)組的值御毅,而不需要修改根欧,使用 *const 類型 arrray ** 只讀指針
double average(const double* array,size_t count){}
指定指針為const有兩個(gè)結(jié)果,
- 編譯器檢查函數(shù)體中的代碼端蛆,確保不會(huì)試圖修改指針?biāo)赶虻闹担ㄗx指針)
- 允許用指向一個(gè)常量的實(shí)參來調(diào)用函數(shù)
把多維數(shù)組傳給函數(shù)
函數(shù)原型:
double yield(const double values[][4],size_t n);
最好不要在原型的第一個(gè)緯度上寫數(shù)字凤粗,可以通過編譯器自動(dòng)推斷,但是后續(xù)的緯度必須寫
double yield(const double array[][4], size_t size);
void proj3()
{
double beans[3][4]{
{1.0,2.0,3.0,4.0},
{5.0,6.0,7.0,8.0},
{9.0,10.0,11.0,12.0} };
cout << "yield = " << yield(beans, size(beans)) << endl;
}
//多維數(shù)組并不適合使用指針表示法
double yield(const double array[][4], size_t size)
{
double sum{};
for (size_t i = 0; i < size; i++)
{
for (size_t j = 0; j < std::size(array[i]); j++)
{
sum += array[i][j];
}
}
return sum;
}
note:一層數(shù)組不要使用size()今豆,編譯器無法推斷 a[][num]的第一個(gè)維數(shù)嫌拣,因?yàn)樾螀⒅皇潜4媪藬?shù)組的地址,并不知道數(shù)組的緯度情況
按引用傳送
對(duì)應(yīng)與引用參數(shù)的實(shí)參不會(huì)復(fù)制呆躲,引用參數(shù)用實(shí)參初始化异逐,它是調(diào)用函數(shù)中該實(shí)參的別名
eg: 引用string string&
類型&
對(duì)于類類型這種,按值傳送實(shí)參和包含因用的函數(shù)傳參是沒有區(qū)別的插掂,但是按值傳送會(huì)對(duì)值進(jìn)行復(fù)制灰瞻,造成內(nèi)存的浪費(fèi)
-
對(duì)比引用與指針
- 最明顯的區(qū)別:傳送指針的時(shí)候,先使用&獲取一個(gè)值的地址辅甥,而在函數(shù)內(nèi)需要使用*解引用
- 指針的鮮明特點(diǎn):可以為nullptr酝润,而引用必須為某個(gè)值。因此允許實(shí)參為null璃弄,就不能使用引用
- 使用引用可以寫出更優(yōu)雅的語法袍祖,但是if不看原型,分辨不清是否應(yīng)用了引用谢揪,(和按值傳送的時(shí)候長的一模一樣)
- 一般認(rèn)為const int& 比 const int *d更好用
在使用指針之前必須測(cè)試指針是否為nullptr蕉陋,而引用一般不需要擔(dān)心
-
對(duì)比輸入輸出參數(shù)
note: 一般不要使用 即作為輸入?yún)?shù),又作為輸出參數(shù)的變量
- 輸入?yún)?shù):一般兩個(gè)選擇:const引用傳送:const string&(建議使用) 按值傳送:string (會(huì)復(fù)制string對(duì)象)
- 可以將T值傳送給T&和constT&引用拨扶,但是只能將const T 值傳送給const T&引用(當(dāng)然其他也可以傳送給他凳鬓,比如T 表示別名)
-
按引用傳送數(shù)組
優(yōu)點(diǎn):不進(jìn)行復(fù)制。能夠修改原始值
可以通過傳遞引用向函數(shù)傳遞精確的多維數(shù)組一維的個(gè)數(shù)
double average10(const double (&array)[10]); //此時(shí)可以在函數(shù)中使用size()
note: 關(guān)于引用的隱式轉(zhuǎn)化問題:
會(huì)有一段臨時(shí)空間實(shí)現(xiàn)相互轉(zhuǎn)換患民,但是double->int時(shí)會(huì)出現(xiàn)丟失缩举,所以會(huì)有報(bào)錯(cuò)
void double_it(const double& it) {//針對(duì)于這種帶const的會(huì)涉及底層轉(zhuǎn)換 cout<<it<endl; } int age{19}; double_it(age);//報(bào)錯(cuò)
新的const string引用
c++17 #include<string_view>
string_view() 具有常量特性,他只需要指向某個(gè)實(shí)際對(duì)象:string 字符串字面量或其他任何字符數(shù)組中存儲(chǔ)的字符序列匹颤,仅孩,初始化和復(fù)制string_view 的開銷都特別低
-
隱式創(chuàng)建string會(huì)復(fù)制字符,但是創(chuàng)建string_view則不會(huì)
string_view sv{"hjkl"};
string_view 不允許直接將其轉(zhuǎn)化為string印蓖,因?yàn)榈讓由婕癱har[]的轉(zhuǎn)化辽慕,必須進(jìn)行強(qiáng)制類型轉(zhuǎn)化
string_view 的幾點(diǎn)注意事項(xiàng):
- 不完全等同于const string ,沒有提供c_str()來轉(zhuǎn)化為一個(gè)const char* 的數(shù)組赦肃。但是提供了data()函數(shù)溅蛉,于此功能等效
- 不能使用+運(yùn)算符連接string_view 可以先轉(zhuǎn)化成string再連接eg: string{view};
- string_view 可以從c樣式的字符數(shù)組創(chuàng)建公浪,大小可以任意大
默認(rèn)實(shí)參值
可在函數(shù)原型中指定默認(rèn)實(shí)參值,但是要按照優(yōu)先級(jí)進(jìn)行指定
void proj4()
{
showError();
}
void showError(string messages)
{
cout << messages << endl;
}
從函數(shù)中的返回值
可以從函數(shù)中返回任意類型的值船侧。主要討論返回指針和引用中的陷阱
返回指針
在指針返回到調(diào)用函數(shù)的時(shí)欠气,指針指向的變量必須仍然在其作用域中。
警告:不要從函數(shù)返回在棧上分配的自動(dòng)局部變量的地址
- 不在調(diào)用程序中的地址
int* larger(int a, int b)
{
//這些地址不在調(diào)用程序中,所以都是錯(cuò)誤的
if (a > b)
{
return &a;//wrong!
}
else
{
return &b;//wrong!
}
}
- 在調(diào)用程序中的地址
int* larger(int* a, int* b)//傳來了地址由指針接收
{
if (*a > *b)
{
return a;//來源于調(diào)用程序的地址返回镜撩,是可以的
}
else
{
return b;//ok
}
}
一個(gè)案例预柒,求一組數(shù)據(jù)的標(biāo)準(zhǔn)化
在.0-1.0之間的一組數(shù)
步驟:
-
減去最小值(查找最小值,每個(gè)數(shù)字 - 最小值)
//查找最小值 const double* smallest(const double data[], size_t count)//不會(huì)對(duì)最小值進(jìn)行更改袁梗,讀指針 {//我只是用這個(gè)值而已 if (count==0) { return nullptr; } size_t index_min{}; for (size_t i = 1; i < count; i++) { if (data[index_min]>data[i]) { index_min = i; } } return &data[index_min]; } //使用最小值宜鸯,調(diào)整數(shù)組 double* shift_range(double data[], size_t count, double delta) { for (size_t i = 0; i < count; i++) { data[i] += delta; } return data; }
-
每個(gè)元素除以max可以將數(shù)組映射到0-1(找到max,除以max)
//查找最小值 const double* largest(const double data[], size_t count)//不會(huì)對(duì)最小值進(jìn)行更改围段,讀指針 { if (count == 0) { return nullptr; } size_t index_max{}; for (size_t i = 1; i < count; i++) { if (data[index_max] < data[i]) { index_max = i; } } return &data[index_max]; } //除以最大值 double* scale_range(double data[], size_t count, double divisor) { if (divisor == 0) { return data; } for (size_t i = 0; i < count; i++) { data[i] /= divisor; } return data; }
void show_data(const double data[], size_t count = 1, string title = "data values", size_t width = 10, size_t perline = 5);
double* normalize_range(double data[], size_t count);
const double* largest(const double data[], size_t count);//不會(huì)對(duì)最小值進(jìn)行更改,讀指針
const double* smallest(const double data[], size_t count);//不會(huì)對(duì)最小值進(jìn)行更改投放,讀指針
double* scale_range(double data[], size_t count, double divisor);
double* shift_range(double data[], size_t count, double delta);
int* larger(int a, int b);
int main()
{
//proj1();
proj2();
return 0;
}
void proj2()
{
double samples[]{
11.0,23.0,13.0,4.0,
57.0,36.0,317.0,88.0,
9.0,100.,121.0,12.0 };
const size_t count{ size(samples) };
show_data(samples,count,"original values");
normalize_range(samples, count);
show_data(samples, count, "normalized values", 12);
}
void show_data(const double data[], size_t count, string title, size_t width, size_t perline)
{
cout << title << endl;
for (size_t i = 0; i < count; i++)
{
cout << setw(width) << data[i];
if ((i+1)%perline==0)
{
cout << '\n';
}
}
cout << endl;
}
const double* largest(const double data[], size_t count);
const double* smallest(const double data[], size_t count);
double* shift_range(double data[], size_t count, double delta);
double* scale_range(double data[], size_t count, double divisior);
double* normalize_range(double data[], size_t count);
//規(guī)范化
double* normalize_range(double data[], size_t count)
{
return scale_range(shift_range(data, count, -(*smallest(data, count))), count, *largest(data, count));
}
//查找最小值
const double* largest(const double data[], size_t count)//不會(huì)對(duì)最小值進(jìn)行更改奈泪,讀指針
{
if (count == 0)
{
return nullptr;
}
size_t index_max{};
for (size_t i = 1; i < count; i++)
{
if (data[index_max] < data[i])
{
index_max = i;
}
}
return &data[index_max];
}
//除以最大值
double* scale_range(double data[], size_t count, double divisor)
{
if (divisor == 0)
{
return data;
}
for (size_t i = 0; i < count; i++)
{
data[i] /= divisor;
}
return data;
}
//查找最小值
const double* smallest(const double data[], size_t count)//不會(huì)對(duì)最小值進(jìn)行更改,讀指針
{
if (count==0)
{
return nullptr;
}
size_t index_min{};
for (size_t i = 1; i < count; i++)
{
if (data[index_min]>data[i])
{
index_min = i;
}
}
return &data[index_min];
}
//使用最小值灸芳,調(diào)整數(shù)組
double* shift_range(double data[], size_t count, double delta)
{
for (size_t i = 0; i < count; i++)
{
data[i] += delta;
}
return data;
}
返回引用
不要從函數(shù)中返回自動(dòng)局部變量的引用
想要返回對(duì)某個(gè)實(shí)參的非const引用涝桅,就不能把參數(shù)指定為const
參數(shù)不是const,所以不能把字符串字面量用作實(shí)參烙样,編譯器不允許這么做
在使用類創(chuàng)建自己的數(shù)據(jù)類型時(shí)冯遂,引用返回類型是必不可少的
返回類型的推斷
類型推斷可以節(jié)省時(shí)間:return 之后的數(shù)據(jù)類型要明確
auto 不會(huì)推斷為一個(gè)引用類型,而總是推斷為一個(gè)值的類型谒获,即使將一個(gè)引用賦值給auto蛤肌,值也會(huì)被復(fù)制。而且值的這個(gè)副本不是const,除非使用const auto
要讓編譯器推斷一個(gè)引用類型批狱,可以使用auto& 或const auto&
函數(shù)體中的靜態(tài)變量
函數(shù)體中的靜態(tài)變量在第一次執(zhí)行的 時(shí)候回初始化裸准,后續(xù)調(diào)用的時(shí)候不會(huì)在初始化,而只是進(jìn)行操作赔硫,變量始終存在于程序中炒俱,直到程序結(jié)束
普通變量聲明帶有垃圾值
靜態(tài)變量初始化會(huì)附帶0值
內(nèi)聯(lián)函數(shù)
內(nèi)聯(lián)函數(shù)的定義通常放在頭文件中,該頭文件包含在使用該函數(shù)的每個(gè)源文件中爪膊。即使用該函數(shù)的每個(gè)源文件都應(yīng)該對(duì)其進(jìn)行引用
inline int larger(int m,int n)
{
return m > n ? m : n ;
}
函數(shù)重載
同名权悟,但是參數(shù)列表不同即可
參數(shù)列表的幾種情況很難判斷
重載和指針參數(shù)
int* 類型處理起來和int[]的參數(shù)類型相同
int* p == int p[];
編譯器會(huì)無視指定數(shù)組維數(shù)的方法,如果需要指定維數(shù)推盛,可以指定array<>或者按照引用傳遞
重載和引用參數(shù)
如下原型
void do_it(stirng number);
void do_it(string& number);
編譯器表示很懵逼峦阁,do_it(124);到底調(diào)用的是誰?
所以不能根據(jù)type1 和type1& 來區(qū)分?jǐn)?shù)據(jù)類型
note: 臨時(shí)地址可以用{}來初始化const耘成,而不能用{}初始化非const
重載和const參數(shù)
帶有const參數(shù)和沒有const的參數(shù)唯一的區(qū)別就是拇派,為引用定義參數(shù)還是為指針定義參數(shù)
-
對(duì)于基本類型 int和const int 是相同的
//下面兩個(gè)原型沒有區(qū)別 long larger(long a ,long a); long larger(const long a ,const long b);//編譯器會(huì)忽略這個(gè)const
-
重載和const指針參數(shù)
在兩個(gè)重載函數(shù)中荷辕,一個(gè)函數(shù)的參數(shù)是type *(會(huì)修改值),另一個(gè)函數(shù)的參數(shù)類型是const type *(一個(gè)只讀指針件豌,不會(huì)修改) 這兩個(gè)函數(shù)是不同的
-
編譯器不會(huì)把const值傳送給非const指針參數(shù)的函數(shù)
const 值會(huì)傳送給const指針
const int* larger(const int* a,const int* b); const int num{0}; *larger(&num)
-
一個(gè)函數(shù)的參數(shù)類型是“指向type的指針”疮方,另一個(gè)函數(shù)的參數(shù)類型是“指向type的const指針”
這兩個(gè)函數(shù)就是相同的
long* larger(long* const a,long* const b);//常量a指向long類型(不能指向別的了) const long* larger(const long* const a,const long* const b);//常量a指向常量的long類型
-
重載和const引用參數(shù)
不允許在&后面添加const
T& (const常量無法賦值給T)和const T&(可以接受const和非const)是不同的