2020-12-25

C++17入門經(jīng)典上

Chapter 1 基本概念

  1. 頭文件包含許多內(nèi)容,其中包括.cpp文件中的可執(zhí)行代碼使用的函數(shù)原型,以及使用的類和模板的定義
  2. 預處理指令會以某種方式修改源代碼,之后會把他們編譯為可執(zhí)行的形式
  3. 頭文件的內(nèi)容會添加到源文件中
  4. 頭文件的內(nèi)容被插入到#include指令的位置
  5. 流是數(shù)據(jù)源或數(shù)據(jù)接收器的一種抽象表示,每個流都關(guān)聯(lián)著某臺設(shè)備,關(guān)聯(lián)著數(shù)據(jù)源的流就是輸入流,關(guān)聯(lián)著目的地的流就是輸出流
  6. 名稱空間類似于姓氏
  7. 兩個冒號::有一個非常奇特的名稱,作用域解析運算符
  8. main函數(shù)不能定義在名稱空間中,未在名稱空間中定義的內(nèi)容都存在于全局名稱空間中,全局名稱空間沒有名稱
  9. 不要使用以下劃線開頭的名稱
  10. 有時候程序需要幾個類似的類或函數(shù),其代碼中只有所處理的數(shù)據(jù)類型有區(qū)別,編譯器使用模板給特定的自定義類型自動生成類或函數(shù)代碼

Chapter 2 基本數(shù)據(jù)類型

  1. 花括號稱為初始化列表,初始化列表可以包含幾個值;優(yōu)勢:允許以相同的方式初始化所有變量,被稱為統(tǒng)一初始化
    1. 可以在花括號中使用單個值來初始化任何變量 c++17
int apple_count{12};    
int counter{};//零初始化 {}相當于0
const unsigned toe_count{10};//常量
  1. const 可以固定任何類型的變量值,不可被修改
  2. unsigned 是無符號數(shù),永遠不會是負值
  3. 整數(shù)除法返回的是分母除以分子得到的倍數(shù),任何余數(shù)都會被舍棄
  4. 浮點類型數(shù)據(jù)不能使用unsigned或signed修飾符,浮點類型總是帶符號的
  5. cmath頭文件中的所有函數(shù)可接受任意浮點類型或整型參數(shù)
    1. abs(arg)
    2. ceil(arg)
    3. floor(arg)
    4. exp(arg)
    5. sqrt(arg)
    6. round(arg)
    7. pow(arg1,arg2)
    8. tan(angle)
  6. iomanip頭文件,控制數(shù)據(jù)格式
    1. fixed 用小數(shù)點固定格式
    2. dec 十進制格式
    3. left 左對齊格式
    4. setprecision(n) 一共n位輸出,if有fixed,則表示小數(shù)點后n位數(shù)字
    5. setw(n) 輸出序列寬度
    6. setfill(ch) ch填充多余,默認是空格
  7. static_cast<要轉(zhuǎn)的新類型>(數(shù)據(jù)) 強制類型轉(zhuǎn)換
  8. auto 關(guān)鍵字可以告訴編譯器應推斷數(shù)據(jù)類型
auto num{10};   

Chapter 3 處理基本數(shù)據(jù)類型

  1. 枚舉 定義:
enum class 類名:指定成員的類型{枚舉成員1,...,枚舉成員n};//默認枚舉成員1 = 0,后一個比前一個大1
//           成員類型可以省略不寫

可以當成類來看待,就是一個類名數(shù)據(jù)類型,引用枚舉時,必須使用類型名(類名)來限定他

枚舉成員的值必須是編譯器可以計算出來的常量表達式,這種表達式包括字面量,以前定義的枚舉成員,聲明為const的變量,不能使用非const變量(即使使用字面量初始化也不行)

enum class Punctuation : char{Comma=',',Exclamation='!',Question='?'};
  1. using關(guān)鍵字允許把自己的數(shù)據(jù)類型名稱指定為另一個類型的替代名稱
using BigOnes = unsigned long long;
  1. 全局變量的初始化在main()開始之前進行,默認情況下被初始化為0.局部變量將全局變量隱藏時,使用作用域解析運算符(::)來限定它

Chapter 4 決策

  1. cctype頭文件

    1. isupper(c) c是否是大寫字母
    2. isalpha(c) c是否是字母
    3. isdigit(c) c是否是數(shù)字
    4. isspace(c) c是否是空白,\n \t \r \f ' '
    5. isblank(c) c是否是空格 ‘ ’ \t‘
    6. tolower(c) 返回c的小寫
    7. isalnum(c) if c 包含字母或數(shù)字,就返回一個正整數(shù)(true)else 0(false)
  2. if(變量){} == if(變量!=0) == if(變量!=false) //內(nèi)! 外==

  3. if(!變量){} == if(變量==0) == if(變量==false)

Chapter 5 數(shù)組和循環(huán)

數(shù)組

  1. 數(shù)組的大小必須用常量表達式來指定
  2. 數(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};
  1. size_t 是某個不帶符號的整數(shù)類型的別名,足夠大,能夠容納編譯器支持的任何類型(包括數(shù)組)
  2. 幻數(shù):產(chǎn)量值的多次使用直接定義為const
  3. 數(shù)組大小一般設(shè)置為unsigned,一般不會為負值
  4. std::size(array); 獲取數(shù)組的大小,c++17
  5. c++可以使用浮點數(shù)來控制for循環(huán)
//無限循環(huán)中,輸入指定個數(shù)元素
x[count] = input;
if(++count == size){
    cout<<"無法容納"<<size<<"個元素"<<endl;
    break;
}
  1. 無符號數(shù)減去值時候應該小心,0-1=numice_limits<size>::max()
  2. 字符數(shù)組初始化字符串字面量,默認會加上 ’\0‘
char name{"aeiou"};
//6 elements
//因為字符串的最后添加了'\0'來標記字符串結(jié)束,所以數(shù)組包含6個元素
  1. 使用數(shù)組名不能輸出數(shù)值類型的數(shù)組的內(nèi)容,這種方法僅僅用于輸出char數(shù)組,即使是傳送給輸出流的char數(shù)組,也必須用空字符結(jié)束,否則程序很可能崩潰
const int maxLength{100};
char text[maxLength]{}
for(int i{};text[i]!='\0';i++){//一個字符數(shù)組中的原因個數(shù)
    if(isalpha(text[i])){
        switch(tolower(text[i])){
            case 'a':case 'e':case 'i': case 'o' :case 'u':
            ++vowels;
            break;
            default:
                ++consonants;
        }
    }
}
  1. 不能使用cin>>直接讀取輸入內(nèi)容,因為>>不能碰到空格
  2. cin.getline(text,maxlength);
  3. 多維數(shù)組和一維的類似,空初始化列表是將其初始化為0
  4. 多維數(shù)組除了第一個緯度之外,必須指定大小,編譯器只能推斷第一個緯度的大小
  5. 在運行期間給數(shù)組分配內(nèi)存空間
cin>>count;
unsigned int height[count];
//此時數(shù)組無法初始化,因為不知道有多少元素。
//因此這種帶有變量個數(shù)的數(shù)組,只能先聲明,后賦值,不可直接初始化

數(shù)組的替代品(優(yōu)于數(shù)組)

std::array

array<T,N>

include<array>

  1. if 創(chuàng)建array<>容器但不指定初始值,則數(shù)組也包含垃圾值
  2. fill()函數(shù)把所有元素設(shè)置為某個給定的值
array<double,100> values{};
values.fill(3.1415);// all elements to pi
  1. array.size(); 返回數(shù)組的元素個數(shù)size_t類型,array對象總是可以通過size()函數(shù)確定自己的大小
  2. array.at(i); 返回i出的值,i是一個索引
  3. array.front();返回數(shù)組第一個值
  4. array.back();返回數(shù)組最后一個值
  5. 兩個array只要容器大小相同,存儲類型相同,可以使用== != > < 對兩個array中的元素進行逐個比對,也可相互賦值

std::vector<T>

  1. 大小可自動增加,可容納任意數(shù)量元素
  2. push_back(X);向vector中添加元素
vector<long> numbers(20,99L);//理解為構(gòu)造器(),20個 99
vector<int> number(20);//有20個0
vector<long> number{20};//只有一個20
vector<unsigned int> primes{1,2,3,4,4,4};//初始化{}列表
  1. 與array類似,兩個vector也可以比較,但是vector可以元素數(shù)量不同的比,字典序比較
  2. 一個vector給另外一個vector賦值時,被賦值的會被覆蓋已經(jīng)存在的值
  3. vector可以存儲在其他容器中
  4. vector沒有fill()成員,但是提供了assign函數(shù),可用于重新初始化vector<>內(nèi)容
vector<long> numbers(20,99L);//20個99
numbers.assign(99,20L);//99個20
numbers.assign({99L,20L});//99 和 20
numbers.clear();//清空numbers
numbers.empty()//vector中是空,沒有數(shù)據(jù)嗎?返回true orfalse
numbers.pop_back();//刪除vector最后一個元素

如果編譯時知道元素的準確數(shù)量,就是用array<>,如果不知道就是用vector<>

Chapter 6 指針和引用

指針

  1. 指針是可存儲地址的變量。他指向內(nèi)存中存儲了其他值的位置
  2. 初始化列表為空,所以這個語句把pnumber初始化為等價于0的指針,即不指向任何內(nèi)容的地址,等價于0的指針寫為nullptr
long* pnumber{nullptr};
long *pnumber{nullptr};
  1. 定義指針時,總是要初始化他,如果還不能為指針提供期望的值,就將其初始化為nullptr
  2. 最好在單獨的代碼行上聲明指針和普通變量,以避免出現(xiàn)這種混淆。
  3. 不管指針指向什么類型或大小的數(shù)據(jù),指針變量本身的大小始終是相同的,如今的平臺上指針變量大小一般是4或8字節(jié)

地址運算符&

  1. &是一個一元運算符,他可以獲取變量的地址。
  2. &運算符可以應用于任何類型的變量,但必須在對應類型的指針中存儲地址
  3. 使用編譯器推斷指針類型,但是盡量還是使用auto*
auto* number{&height};
  1. 使用auto*聲明的變量只能用指針值初始化,使用其他類型的值初始化,會導致編譯錯誤。

間接運算符

  1. 將間接運算符*應用于指針,可以訪問指針所指向的內(nèi)存位置的數(shù)據(jù),也成為為解引用運算符
int count{};
int* pount{&count};
  1. *是乘法運算符,間接運算符,還可以應用于聲明指針,一般編譯器根據(jù)上下文分析
  2. 使用指針的用處
    1. 動態(tài)的為新變量分配內(nèi)存空間,即可以在程序執(zhí)行過程中分配
    2. 使用指針表示法操作存儲在數(shù)組中的數(shù)據(jù),與普通數(shù)組表示法完全等效
    3. 指針可以在函數(shù)中訪問函數(shù)外部定義的大塊數(shù)據(jù)
    4. 指針是支持多態(tài)性起作用的基礎(chǔ)

char類型的指針

  1. 指向char類型的變量,可以用字符串字面量(實際上是const char類型的數(shù)組)初始化
  2. 給未解除引用的char類型指針應用<<插入運算符-----假定這種指針包含以空字符結(jié)尾的字符串的地址
  3. 如果給解除引用的char類型指針應用<<插入運算符---將地址中的單個字符寫入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;//輸出首字符(首地址)   解除引用
}
  1. 指針數(shù)組

每個因子都是一個const char* ,一個const char* 指向一個字符串字面量

其中的*pstr[i] 無法進行再賦值,因為其為常量const,無法進行修改

void proj2()
{
    const char* pstars[] = {//默認都會加上\0
        "fatty","clara","lassie","slim","boris","mae","oliver","greta"
    };
    //pstars[]數(shù)組中的每個元素 會指向一個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)部可以相互賦值,就很離譜!!
}//指針數(shù)組法

把一個聲明從右向左讀。

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];
  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是一個常量,不能修改,pvalue是一個指向常量的指針,可以用于存儲value的地址。不能在非const int指針中存儲value的地址。

但是可以把非const變量賦給pvalue、

  1. 常量指針

存儲在指針中的地址不能修改。

只能指向初始化時指定的地址。

但是地址的內(nèi)容不是常量,可以修改,

int data{ 20 }; 
int* const pdata{ &data }; 
*pdata = 25;//內(nèi)容可變
  1. 指向常量的常量指針

因為存儲在指針中的地址和指針指向的內(nèi)容都被聲明為常量,所以兩者都不能修改

const float value{3.14};
const float* const pvalue{&value};

指針和數(shù)組

  1. 數(shù)組名可以像指針那樣操作,輸出時,使用非char類型的數(shù)組名,就可以得到內(nèi)存中的地址。
int num[]{ 1,2,3 };
cout << num << endl;
  1. 指針可進行的運算:

    1. 加減:(在數(shù)組中表現(xiàn)為向后/前移動一位,就是一個類型的字節(jié)數(shù))

      • 指針+整數(shù)=指針

        int data[]{1,2,4};
        int* pdata{&data[1]};
        cout<<* (pdata+1)<<endl;//4
        
      • 指針-整數(shù)=指針

      • 指針+-指針=整數(shù)(類型相同,同一數(shù)組中)

      結(jié)果是兩個索引的差值

    2. 比較

動態(tài)內(nèi)存分配

  1. 所有指針都應該初始化,如果指針沒有包含合法的地址,就應該總讓他包含nullptr
double* pvalue{new double{}};//初始化為0.0
double* pnull{};//初始化為nullptr
delete pvalue;
pvalue = nullptr;
  1. delete 只是將自由存儲區(qū)的內(nèi)存釋放,變量還可以使用,但是最好先將其置為nullptr

  2. 與普通數(shù)組不同的是,無法讓編譯器推斷出動態(tài)分配數(shù)組的維數(shù)

double* data{new double[100]};//100個垃圾值
int* num{new int[100]{0}};//100個0
int* one{new int[]{1,2,3}};//報錯,無法推斷
delete[] data;//釋放動態(tài)數(shù)組的內(nèi)存
data=nullptr;
  1. 釋放動態(tài)數(shù)組的內(nèi)存,使用delete[] 或者delete [] 方括號表示刪除的數(shù)組
  2. ->指針選擇成員
  3. 多維動態(tài)數(shù)組?
void proj6()
{
    int rows{ 3 };
    int columns{ 3 };
    //動態(tài)二維數(shù)組
    //carrots數(shù)組是double*指針的一個動態(tài)數(shù)組,每個double*指針包含一個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;
}
  1. 每個new必須對應一個delete,每個new[] 必須對應一個delete[]
  2. 在c++程序設(shè)計中,盡量不要使用new delete new[] delete[],應該使用vector<> 和智能指針來規(guī)避動態(tài)內(nèi)存的一些風險。盡量避免直接操作動態(tài)內(nèi)存。

智能指針

  1. 不必使用delete delete[] 運算符釋放內(nèi)存

  2. 智能指針不能進行遞增或遞減,也不能進行算術(shù)操作

  3. <memory>頭文件

  4. 三種智能指針

    1. unique_ptr<T>

      1. 這個對象類似于指向T類型的指針,是惟一的。
      2. 從不會有兩個unique_ptr<>對象指向同一地址
      3. 指向的值被該對象獨占
      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) };//
      //三個方法相同,推薦最后一種寫法,簡潔,且可以防止內(nèi)存泄漏
      
      1. reset() 將指針重置為nullptr,如果參數(shù)不設(shè)置值,就是nullptr

      2. release() 將智能指針轉(zhuǎn)換為普通指針,注意,將其轉(zhuǎ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) };//動態(tài)創(chuàng)建n個元素的數(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;
        
  1. shared_ptr<T>

    1. 創(chuàng)建shared_ptr<T> 過程更復雜一些,主要是需要維護引用計數(shù),

      void proj8()
      {
         shared_ptr<double> pdata{ new double{111.1} };
         shared_ptr<double> pdata2;//初始化為nullptr
         pdata2 = pdata;
         cout << *pdata2 << endl;
      }
      
      1. 復制pdata會增加引用次數(shù),兩個指針必須重置或釋放,double變量占用的內(nèi)存才會釋放
      2. 實際使用共享指針的情況通常涉及對象
  2. weak_ptr<T>

理解引用

  1. 引用就是一個別名,可以用作某對象的別名,不可以只聲明引用而不對其初始化
  2. 引用不能修改另一個另一個對象的別名
  3. 類型名后面&符號表示引用,如果取得引用的地址,結(jié)果會是指向原始變量的一個指針

就當做別名去理解,想不清楚就用原名

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 操作字符串

  1. c++的string頭文件定義了string類型,相比ctring頭文件中以‘\0’結(jié)尾的c字符串更可靠

  2. 定義string對象的六種方式

    1. empty
    2. 字面量
    3. 字面量切割
    4. 構(gòu)造器
    5. string對象
    6. string對象切割
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個a 構(gòu)造器

string sentence{proverb}; //string 對象

string sentence{proverb,0,13};//(begin,num) //string對象的切割

  1. c串與string串的轉(zhuǎn)化,(string 字符串的長度不會計算\0,但是有\(zhòng)0存在(不理它))

    1. string.c_str();
    2. 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();

  1. string對象的操作

    1. 賦值
      • 字面量賦值
      • 串變量賦值
    string adj{"hornwogging"};//字面量賦值
    string word{"ribbish"};
    word{adj};//串變量賦值
    adj = "twotiming";
    
    1. 連接
    • +連接:必須有一個string對象在+的一側(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(,'!');//!!!
    
    1. 數(shù)串連接:

    ? to_string(數(shù)字)+字符串

    double num{ 100.0 };
    string name{ "I'm Huang Hongwei.I am " };
    cout << name + to_string(num) + " years old" << endl;
    
  2. 讀入串getline(cin,text)

    string text{};
    getline(cin, text);
    cout << text << endl;
    
  3. 訪問子串

    • 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;
      
  1. 比較字符串

    1. 字典序比較:

      • 前面的字符都相同,看長度,越長越大
      • 長度相同,且對應字符相同,則相同
    2. string對象可以存儲到容器中,普通的char數(shù)組不能存儲到容器中。std空間定義了一個非成員函數(shù)模板,實現(xiàn)效果與swap(a,b)相同

    3. compare()函數(shù)

      • obj. compare()函數(shù)可以比較該對象

        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;
            }
        }
        
    4. 使用substr()進行比較

    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;
     }
    }
    

    ?

  2. 搜索字符串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(串,對象的初始查找點)
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;//返回集合中(有一個匹配就返回)第一個出現(xiàn)的位置 5
    
  • find_first_not_of() find_last_not_off()

    搜索不在字符集合中的字符的位置

    cout<<text.find_first_not_of("aeiouAEIOU");//查找第一個不是元音的位置
    
  • 逆向搜索字符串str.rfind();

string sentence{"manners maketh man"};
string word{"an"};
cout<<sentence.rfind(word);//16  從后向前搜索,index=16是an第一次出現(xiàn)的地方
//從n開始搜索

  1. 修改字符串

    1. 插入 :索引位置前插入,充當當前索引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個字符  插入到對象的13位置
      cout << phrase << endl;
      
  1. 替換

    string text{ "we can replace a string" };
    text.replace(1, 5, "123456");//在index=1的地方,的5 個字符,替換為123456
    cout << text << endl;
    
  1. 刪除

    刪除[begin,]之后的len個字符

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ù)列表)

{

}

  1. 函數(shù)調(diào)用中的實參順序必須對應于函數(shù)列表里的參數(shù)順序。函數(shù)名和參數(shù)列表的組合稱為函數(shù)的簽名。
  2. 函數(shù)體中可以有多個return語句,每個return語句可能返回不同的值。
  3. 返回類型是void->return ;
  4. 函數(shù)原型=函數(shù)聲明:定義了函數(shù)名,函數(shù)的返回值,參數(shù)列表;
  5. 聲明一定要放在調(diào)用之前。。除非把實現(xiàn)寫在寫在引用之前。(同c語言)

給函數(shù)傳遞實參

如果指定的函數(shù)實參類型不對應參數(shù)類型,編譯器就會把參數(shù)的類型隱式轉(zhuǎn)換為參數(shù)類型

按值傳送:

實參的變量值或常量值根本不會傳送給函數(shù),而是創(chuàng)建實參的副本,把這些副本傳給函數(shù)。執(zhí)行完函數(shù)之后就廢棄副本(不會對原有的值進行更改)

  1. 給函數(shù)傳遞指針:參數(shù)為指針類型時,按值傳送機制就會像以前那樣運行,但是指針包含另一個變量的地址,此時,指針的副本也包含這樣一個副本
#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ù)組更高效,(不需要復制許多元素),函數(shù)體中的代碼可以把表示數(shù)組的參數(shù)作為指針來看待,即函數(shù)體中可以給數(shù)組參數(shù)使用指針表示法的強大功能

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;
}

注意事項:

  • 不能通過size()來避免指定count值,數(shù)組參數(shù)array只是存儲數(shù)組的地址,并不是數(shù)組本身;

  • 如果使用sizeof(array) 將返回數(shù)組地址的內(nèi)存位置的大小,而不是整個數(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ù)組

如果只想計算10個數(shù)的平均值,

double average10(double array[10])
{
    double sum{};
    for (size_t i = 0; i < 10; i++)
    {
        sum += array[i];
    }
    return sum / 10;
}

這個函數(shù)的簽名啊雖然合法,但是呢,會給人一種錯誤的期待

編譯器會強制將剛好包含10個元素的數(shù)組作為實參傳遞給該函數(shù)

double average10(double array[10])
double average10(double array[])
double average10(double* array)
//等價的三種方式
//指定維數(shù)的數(shù)組如果本例中只輸入一個帶有三個元素的數(shù)組,那么仍然會使用10個去計算,下標會越界

const 指針參數(shù)

只需要訪問數(shù)組的值,而不需要修改,使用 *const 類型 arrray ** 只讀指針

double average(const double* array,size_t count){}

指定指針為const有兩個結(jié)果,

  • 編譯器檢查函數(shù)體中的代碼,確保不會試圖修改指針所指向的值(讀指針)
  • 允許用指向一個常量的實參來調(diào)用函數(shù)

把多維數(shù)組傳給函數(shù)

函數(shù)原型:

 double yield(const double values[][4],size_t n);

最好不要在原型的第一個緯度上寫數(shù)字,可以通過編譯器自動推斷,但是后續(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]的第一個維數(shù),因為形參只是保存了數(shù)組的地址,并不知道數(shù)組的緯度情況

按引用傳送

對應與引用參數(shù)的實參不會復制,引用參數(shù)用實參初始化,它是調(diào)用函數(shù)中該實參的別名

eg: 引用string    string&

類型&

  1. 對于類類型這種,按值傳送實參和包含因用的函數(shù)傳參是沒有區(qū)別的,但是按值傳送會對值進行復制,造成內(nèi)存的浪費

  2. 對比引用與指針

    1. 最明顯的區(qū)別:傳送指針的時候,先使用&獲取一個值的地址,而在函數(shù)內(nèi)需要使用*解引用
    2. 指針的鮮明特點:可以為nullptr,而引用必須為某個值。因此允許實參為null,就不能使用引用
    3. 使用引用可以寫出更優(yōu)雅的語法,但是if不看原型,分辨不清是否應用了引用,(和按值傳送的時候長的一模一樣)
    4. 一般認為const int& 比 const int *d更好用

    在使用指針之前必須測試指針是否為nullptr,而引用一般不需要擔心

  3. 對比輸入輸出參數(shù)

    note: 一般不要使用 即作為輸入?yún)?shù),又作為輸出參數(shù)的變量

  • 輸入?yún)?shù):一般兩個選擇:const引用傳送:const string&(建議使用) 按值傳送:string (會復制string對象)
  • 可以將T值傳送給T&和constT&引用,但是只能將const T 值傳送給const T&引用(當然其他也可以傳送給他,比如T 表示別名)
  1. 按引用傳送數(shù)組

    優(yōu)點:不進行復制。能夠修改原始值

    可以通過傳遞引用向函數(shù)傳遞精確的多維數(shù)組一維的個數(shù)

    double average10(const double (&array)[10]);
    //此時可以在函數(shù)中使用size()
    

    note: 關(guān)于引用的隱式轉(zhuǎn)化問題:

    會有一段臨時空間實現(xiàn)相互轉(zhuǎn)換,但是double->int時會出現(xiàn)丟失,所以會有報錯

    void double_it(const double& it)
    {//針對于這種帶const的會涉及底層轉(zhuǎn)換
     cout<<it<endl;
    }
    
    int age{19};
    double_it(age);//報錯
    

新的const string引用

c++17 #include<string_view>

  1. string_view() 具有常量特性,他只需要指向某個實際對象:string 字符串字面量或其他任何字符數(shù)組中存儲的字符序列,,初始化和復制string_view 的開銷都特別低

  2. 隱式創(chuàng)建string會復制字符,但是創(chuàng)建string_view則不會

    string_view sv{"hjkl"};
    
  3. string_view 不允許直接將其轉(zhuǎn)化為string,因為底層涉及char[]的轉(zhuǎn)化,必須進行強制類型轉(zhuǎn)化

string_view 的幾點注意事項:

  • 不完全等同于const string ,沒有提供c_str()來轉(zhuǎn)化為一個const char* 的數(shù)組。但是提供了data()函數(shù),于此功能等效
  • 不能使用+運算符連接string_view 可以先轉(zhuǎn)化成string再連接eg: string{view};
  • string_view 可以從c樣式的字符數(shù)組創(chuàng)建,大小可以任意大

默認實參值

可在函數(shù)原型中指定默認實參值,但是要按照優(yōu)先級進行指定

void proj4()
{
    showError();
}
void showError(string messages) 
{
    cout << messages << endl; 
}

從函數(shù)中的返回值

可以從函數(shù)中返回任意類型的值。主要討論返回指針和引用中的陷阱

返回指針

在指針返回到調(diào)用函數(shù)的時,指針指向的變量必須仍然在其作用域中。

警告:不要從函數(shù)返回在棧上分配的自動局部變量的地址

  1. 不在調(diào)用程序中的地址
int* larger(int a, int b)
{
    //這些地址不在調(diào)用程序中,所以都是錯誤的
    if (a > b)
    {
        return &a;//wrong!
    }
    else
    {
        return &b;//wrong!
    }
}
  1. 在調(diào)用程序中的地址
int* larger(int* a, int* b)//傳來了地址由指針接收
{
    if (*a > *b)
    {
        return a;//來源于調(diào)用程序的地址返回,是可以的
    }
    else
    {
        return b;//ok
    }
}

一個案例,求一組數(shù)據(jù)的標準化

在.0-1.0之間的一組數(shù)

步驟:

  • 減去最小值(查找最小值,每個數(shù)字 - 最小值)

    //查找最小值
    const double* smallest(const double data[], size_t count)//不會對最小值進行更改,讀指針
    {//我只是用這個值而已
      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;
    }
    
  • 每個元素除以max可以將數(shù)組映射到0-1(找到max,除以max)

    //查找最小值
    const double* largest(const double data[], size_t count)//不會對最小值進行更改,讀指針
    {
      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);//不會對最小值進行更改,讀指針
const double* smallest(const double data[], size_t count);//不會對最小值進行更改,讀指針
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)//不會對最小值進行更改,讀指針
{
    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)//不會對最小值進行更改,讀指針
{
    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;
}

返回引用

  1. 不要從函數(shù)中返回自動局部變量的引用

  2. 想要返回對某個實參的非const引用,就不能把參數(shù)指定為const

  3. 參數(shù)不是const,所以不能把字符串字面量用作實參,編譯器不允許這么做

  4. 在使用類創(chuàng)建自己的數(shù)據(jù)類型時,引用返回類型是必不可少的

返回類型的推斷

  1. 類型推斷可以節(jié)省時間:return 之后的數(shù)據(jù)類型要明確

  2. auto 不會推斷為一個引用類型,而總是推斷為一個值的類型,即使將一個引用賦值給auto,值也會被復制。而且值的這個副本不是const,除非使用const auto

  3. 要讓編譯器推斷一個引用類型,可以使用auto& 或const auto&

函數(shù)體中的靜態(tài)變量

函數(shù)體中的靜態(tài)變量在第一次執(zhí)行的 時候回初始化,后續(xù)調(diào)用的時候不會在初始化,而只是進行操作,變量始終存在于程序中,直到程序結(jié)束

普通變量聲明帶有垃圾值

靜態(tài)變量初始化會附帶0值

內(nèi)聯(lián)函數(shù)

內(nèi)聯(lián)函數(shù)的定義通常放在頭文件中,該頭文件包含在使用該函數(shù)的每個源文件中。即使用該函數(shù)的每個源文件都應該對其進行引用

inline int larger(int m,int n)
{
    return m > n ? m : n ;
}

函數(shù)重載

同名,但是參數(shù)列表不同即可

參數(shù)列表的幾種情況很難判斷

重載和指針參數(shù)

int* 類型處理起來和int[]的參數(shù)類型相同

int* p == int p[];

編譯器會無視指定數(shù)組維數(shù)的方法,如果需要指定維數(shù),可以指定array<>或者按照引用傳遞

重載和引用參數(shù)

如下原型

void do_it(stirng number);
void do_it(string& number); 

編譯器表示很懵逼,do_it(124);到底調(diào)用的是誰?

所以不能根據(jù)type1 和type1& 來區(qū)分數(shù)據(jù)類型

note: 臨時地址可以用{}來初始化const,而不能用{}初始化非const

重載和const參數(shù)

帶有const參數(shù)和沒有const的參數(shù)唯一的區(qū)別就是,為引用定義參數(shù)還是為指針定義參數(shù)

  1. 對于基本類型 int和const int 是相同的

    //下面兩個原型沒有區(qū)別
    long larger(long a ,long a);
    long larger(const long a ,const long b);//編譯器會忽略這個const
    
  2. 重載和const指針參數(shù)

    1. 在兩個重載函數(shù)中,一個函數(shù)的參數(shù)是type *(會修改值),另一個函數(shù)的參數(shù)類型是const type *(一個只讀指針,不會修改) 這兩個函數(shù)是不同的

    2. 編譯器不會把const值傳送給非const指針參數(shù)的函數(shù)

      const 值會傳送給const指針

      const int* larger(const int* a,const int* b);
      const int num{0};
      *larger(&num)
      
    3. 一個函數(shù)的參數(shù)類型是“指向type的指針”,另一個函數(shù)的參數(shù)類型是“指向type的const指針”

      這兩個函數(shù)就是相同的

      long* larger(long* const a,long* const b);//常量a指向long類型(不能指向別的了)
      const long* larger(const long* const a,const long* const b);//常量a指向常量的long類型
      
    4. 重載和const引用參數(shù)

      不允許在&后面添加const

      T& (const常量無法賦值給T)和const T&(可以接受const和非const)是不同的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容