C++學(xué)習(xí)筆記(更新)-創(chuàng)新互聯(lián)

前言

本篇文章是個(gè)人的C++學(xué)習(xí)記錄,也算是《C++ Primer Plus》的讀書筆記,如有錯(cuò)誤,期望指正

成都創(chuàng)新互聯(lián)從2013年成立,先為吳堡等服務(wù)建站,吳堡等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為吳堡企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。第一章 預(yù)備知識(shí) 類和對(duì)象

在c++中,類是一種規(guī)范,描述了一種新型數(shù)據(jù)格式,對(duì)象是根據(jù)這種規(guī)范構(gòu)造的特定數(shù)據(jù)結(jié)構(gòu)

也就是說,類是抽象的,描述了有什么數(shù)據(jù)?對(duì)數(shù)據(jù)有哪些操作?而對(duì)象是具體的,是類的實(shí)例化

OOP(面向?qū)ο缶幊?并不僅僅是將數(shù)據(jù)和方法合并成類定義。

  1. 創(chuàng)建可重用的代碼,這將減少大量的工作
  2. 信息隱藏可以保護(hù)數(shù)據(jù),使其免遭不適當(dāng)?shù)脑L問
  3. 多態(tài)使得我們可以為運(yùn)算符和函數(shù)創(chuàng)建多個(gè)定義,通過編程上下文來確定使用哪個(gè)定義
  4. 繼承使得我們能夠使用舊類派生出新類
泛型

模板是一種對(duì)類型進(jìn)行參數(shù)化的工具,模板是泛型編程的基礎(chǔ),而泛型編程指的就是編寫與類型無關(guān)的代碼,是C++中一種常見的代碼復(fù)用方式。模板分為模板函數(shù)和模板類;模板函數(shù)針對(duì)參數(shù)類型不同的函數(shù);模板類主要針對(duì)數(shù)據(jù)成員和成員函數(shù)類型不同的類。
在這里插入圖片描述

以上是對(duì)C++一些相較于C而言比較“新”的東西的總結(jié),也是作者接下來要重點(diǎn)學(xué)習(xí)的地方

第二章 C++ 內(nèi)置數(shù)據(jù)類型 基本類型

整型變量:bool、char、signed char、unsigned char、short、 unsigned short、int、unsigned int、long、unsigned long

浮點(diǎn)型變量:float、double、long double

對(duì)于整型變量,頭文件climits中定義了符號(hào)常量來表示類型的屬性,可自行查看。

對(duì)于浮點(diǎn)類型,只要包含頭文件“stdio.h”即可使用printf進(jìn)行格式化輸出。

C++算術(shù)運(yùn)算符

+、-、*、/、%分別為加、減、乘、除、求余

對(duì)于除法,分別有int類型、long類型、double類型、float類型

#include#include"stdio.h"
using namespace std;
int main(){
    int a=2,b=5;
    float c=3.78343;
    cout<
復(fù)合類型

數(shù)組、C風(fēng)格字符串、string類字符串、結(jié)構(gòu)體、共用體、枚舉、指針、vector類、array類……

數(shù)組

聲明數(shù)組,需要指出:儲(chǔ)存在每個(gè)元素中的值的類型、數(shù)組名、數(shù)組中的元素?cái)?shù)

typeName arrayName[arraySize];
//其中,arraySize指定元素?cái)?shù)目,它必須是整型常數(shù)或const值,也可以是常量表達(dá)式。簡(jiǎn)而言之,不能是變量(程序運(yùn)行過程賦值的量)
//數(shù)組是由其他類型來創(chuàng)建的復(fù)合類型,不能僅僅將某種東西聲明為數(shù)組,它必須是特定類型的數(shù)組。

初始化數(shù)組,只有在定義數(shù)字時(shí)才能使用初始化,以后就不能使用了,也不能把一個(gè)數(shù)組賦給另一個(gè)數(shù)組,但可以用下標(biāo)給元素賦值

int text[2] = {1,2};	float b[5] = {1,2,3};//可以不完全賦值,但不能超額
short things[] = {1,5,3,8};//計(jì)算元素個(gè)數(shù)時(shí),可以用
int num = sizeof things / sizeof (short);

數(shù)組賦值

int main(){
    int a[10];char b[10];string c[10];
    cin>>a;//報(bào)錯(cuò)
    cin>>b;//允許
    cin>>c;//報(bào)錯(cuò)
    return 0;//a=,b=,c=,也報(bào)錯(cuò),不能整體賦值,只能單元素賦值
}//字符串(字符數(shù)組)可以整體cin,其他數(shù)組只能單元素cin
C風(fēng)格字符串

C風(fēng)格字符串的本質(zhì)是一個(gè)以空字符’\0’為結(jié)尾的char數(shù)組,將字符串存儲(chǔ)到數(shù)組中,有兩種方式

  1. 將數(shù)組初始化為字符串常量
char dog[8]={'f','a','t','e','s','s','a','\0'}
char bird[11]="Mr.Cheeps"http://后兩種方式會(huì)自動(dòng)在結(jié)尾添加'\0'
char fish[]="Bubbles"http://兩種方式都要求數(shù)組長(zhǎng)度大于等于字符串加'\0'的長(zhǎng)度
//注意,不能先定義再賦值,如:char a[10];a="XXX";//a是數(shù)組第一個(gè)元素的指針,不能改變值。
//但是可以對(duì)單個(gè)元素賦值,如:char a[10];a[0]='x';

? 拼接字符串,在cout時(shí),任何兩個(gè)由空白(空格、制表符、換行符)分隔的字符串常量,都會(huì)自動(dòng)拼接成一個(gè)。

cout<<"sfsagsg""sagsagasg";cout<<"sagasghsahas"	"asfagasgsa";
cout<<"safsagasdhrwjnf"
"sagasghshdahna";
  1. 將鍵盤或文件輸入讀入到數(shù)組中
const int Arsize=20;
char name[Arsize];
char dessert[Arsize];
cin>>name;cin>>dessert;
cout<<"hello"<

? 面向行的輸入

const int Arsize=20;
char name[Arsize];char dog[Arsize];
cin.getline(name,20);//遇見換行符或者到達(dá)大值(算上換行)停止,吃掉換行符,并把它變成空字符
cin.get(dog,20);//遇見換行符或者到達(dá)大值(算上換行)停止,不吃換行符,把它留在輸入流中
cin.get();//接收留在輸入流中的換行符
cin.get(name,Arsize).get();cin.get(dog,Arsize).get();//與cin.getline(name,Arsize).getline(dog,Arsize);等效
(cin>>name).get();//cin>>name返回的是cin對(duì)象,可以調(diào)用get()
string類
#include#includeusing namespace std;
int main(){//string對(duì)象在很多方面與字符數(shù)組相同,初始化方式可以一樣,可以cin,可以cout,也可以用數(shù)組表示法訪問單個(gè)字符
    string str1;string str2="asdfg";
    str1="sdafasf";str2="asdfgg";
    cout<
運(yùn)算符重載
  1. + 和 +=:連接字符串
  2. =:字符串賦值
  3. >、>=、< 和<=:字符串比較(例如a< b, aa< ab)
  4. ==、!=:比較字符串
  5. <<、>>:輸出、輸入字符串

注意:使用重載的運(yùn)算符 + 時(shí),必須保證前兩個(gè)操作數(shù)至少有一個(gè)為 string 類型。例如,下面的寫法是不合法的:

#include#includeint main()
{string str = "cat";
    cout<< "apple" + "boy" + str; // illegal!
    return 0;
}
查找
string str;
cin >>str;

str.find("ab");//返回字符串 ab 在 str 的位置
str.find("ab", 2);//在 str[2]~str[n-1] 范圍內(nèi)查找并返回字符串 ab 在 str 的位置
str.rfind("ab", 2);//在 str[0]~str[2] 范圍內(nèi)查找并返回字符串 ab 在 str 的位置

//first 系列函數(shù)
str.find_first_of("apple");//返回 apple 中任何一個(gè)字符首次在 str 中出現(xiàn)的位置
str.find_first_of("apple", 2);//返回 apple 中任何一個(gè)字符首次在 str[2]~str[n-1] 范圍中出現(xiàn)的位置
str.find_first_not_of("apple");//返回除 apple 以外的任何一個(gè)字符在 str 中首次出現(xiàn)的位置
str.find_first_not_of("apple", 2);//返回除 apple 以外的任何一個(gè)字符在 str[2]~str[n-1] 范圍中首次出現(xiàn)的位置

//last 系列函數(shù)
str.find_last_of("apple");//返回 apple 中任何一個(gè)字符最后一次在 str 中出現(xiàn)的位置
str.find_last_of("apple", 2);//返回 apple 中任何一個(gè)字符最后一次在 str[0]~str[2] 范圍中出現(xiàn)的位置
str.find_last_not_of("apple");//返回除 apple 以外的任何一個(gè)字符在 str 中最后一次出現(xiàn)的位置
str.find_last_not_of("apple", 2);//返回除 apple 以外的任何一個(gè)字符在 str[0]~str[2] 范圍中最后一次出現(xiàn)的位置

//以上函數(shù)如果沒有找到,均返回string::npos
cout<< string::npos;
子串
str.substr(3); //返回 [3] 及以后的子串
str.substr(2, 4); //返回 str[2]~str[2+(4-1)] 子串(即從[2]開始4個(gè)字符組成的字符串)
替換
str.replace(2, 4, "sz");//返回把 [2]~[2+(4-1)] 的內(nèi)容替換為 "sz" 后的新字符串
str.replace(2, 4, "abcd", 3);//返回把 [2]~[2+(4-1)] 的內(nèi)容替換為 "abcd" 的前3個(gè)字符后的新字符串
插入
str.insert(2, "sz");//從 [2] 位置開始添加字符串 "sz",并返回形成的新字符串
str.insert(2, "abcd", 3);//從 [2] 位置開始添加字符串 "abcd" 的前 3 個(gè)字符,并返回形成的新字符串
str.insert(2, "abcd", 1, 3);//從 [2] 位置開始添加字符串 "abcd" 的前 [2]~[2+(3-1)] 個(gè)字符,并返回形成的新字符串
追加

除了用重載的+操作符,還可以使用函數(shù)來完成。

str.push_back('a');//在 str 末尾添加字符'a'
str.append("abc");//在 str 末尾添加字符串"abc"
刪除
str.erase(3);//刪除 [3] 及以后的字符,并返回新字符串
str.erase(3, 5);//刪除從 [3] 開始的 5 個(gè)字符,并返回新字符串
交換
str1.swap(str2);//把 str1 與 str2 交換
其他
str.size();//返回字符串長(zhǎng)度
str.length();//返回字符串長(zhǎng)度
str.empty();//檢查 str 是否為空,為空返回 1,否則返回 0
str[n];//存取 str 第 n + 1 個(gè)字符
str.at(n);//存取 str 第 n + 1 個(gè)字符(如果溢出會(huì)拋出異常)
實(shí)例

查找給定字符串并把相應(yīng)子串替換為另一給定字符串

string 并沒有提供這樣的函數(shù),所以我們自己來實(shí)現(xiàn)。由于給定字符串可能出現(xiàn)多次,所以需要用到find()成員函數(shù)的第二個(gè)參數(shù),每次查找之后,從找到位置往后繼續(xù)搜索。直接看代碼(這個(gè)函數(shù)返回替換的次數(shù),如果返回值是 0 說明沒有替換):

int str_replace(string &str, const string &src, const string &dest)
{int counter = 0;
    string::size_type pos = 0;
    while ((pos = str.find(src, pos)) != string::npos) {str.replace(pos, src.size(), dest);
        ++counter;
        pos += dest.size();
    }
    return counter;
}

從給定字符串中刪除一給定字串

方法和上面相似,內(nèi)部使用erase()完成。代碼:

int str_erase(string &str, const string src)
{int counter = 0;
    string::size_type pos = 0;
    while ((pos = str.find(src, pos)) != string::npos) {str.erase(pos, src.size());
        ++counter;
    }
    return counter;
}

給定一字符串和一字符集,從字符串剔除字符集中的任意字符

int str_wash(string &str, const string src)
{int counter = 0;
    string::size_type pos = 0;
    while ((pos = str.find_first_of(src, pos)) != string::npos) {str.erase(pos, 1);
        ++counter;
    }
    return counter;
}
結(jié)構(gòu)體

數(shù)組可以將多個(gè)同類型元素存儲(chǔ)在一起,而結(jié)構(gòu)體可以將多種類型的數(shù)據(jù)存儲(chǔ)在一起

struct text{
    char name[20];
    float volume;
    double price;
};//結(jié)構(gòu)體由其他類型數(shù)據(jù)組成,各類型依然遵守原來的用法,初始化方法、賦值方法……
int main(){//可以將一個(gè)結(jié)構(gòu)賦給另一個(gè)結(jié)構(gòu)(他們的類型相同時(shí))
    text a={};
    //a={"asdad","2.5","2.5"};和大多數(shù)(除了string之外)混合類型一樣,不能“賦值”
    cin>>a.name;//可以單元素cin,不能整體cin(cin>>a)
    text b={"sadfa",2.5,2.5};//初始化方式這樣
    //cout<共用體

共用體可以存儲(chǔ)不同的數(shù)據(jù)類型,但只能存儲(chǔ)其中的一種類型

union one4all{
    int int_val;
    long long_val;
    double double_val;
}pail;//可以用pail存儲(chǔ)int、long、double中的一種,條件是在不同的時(shí)間進(jìn)行。

共用體與結(jié)構(gòu)體共用

struct wight{
    char brand[20];int type;
    union id{
        long id_num;
        char id_char[20];
    }id_val;//id_val為組成部分,調(diào)用時(shí)要通過id_val,id_num,id_char為id_val成員,一個(gè)有值另一個(gè)就會(huì)沒值
};
struct other{
    char brand[20];int type;
    union {
        long id_num;
        char id_char[20];
    };//匿名共用體,id_num,id_char為結(jié)構(gòu)體成員,一個(gè)有值另一個(gè)就會(huì)沒值
};//匿名匿的是共用體和共用體變量的名,不單單是共用體變量的名
int main(){
    wight a={};other b={};
    cin>>a.id_val.id_char;
    cin>>b.id_num;
    return 0;//這里共用的作用是,可以根據(jù)type判斷id是那種類型,這樣就減少了空間占用。
}
枚舉
enum spectrum {red,orange,yellow,green,blue,violet,indigo,ultraviolet};
//讓spectrum成為新類型的名稱,它被稱為枚舉,花括號(hào)里的東西變成符號(hào)常量
spectrum a;//則a只能用花括號(hào)里的東西賦值
enum bits {one=1,two=2,three=3};
enum {on=1,tw=2,thre};//枚舉相當(dāng)于多個(gè)const,但是值只能是整數(shù)
指針和NEW 指針
#includeusing namespace std;
int main(){
   int a=3;int* b=&a;//類型名* 指針名=&變量名
   cout<
int a=80;
const int* text0=&a;//指向 const int(常量)的指針
int* const test1=&a;//指向 int 的 const 的指針(指針本身不能被改變)
//注意,可以將const數(shù)據(jù)或非const數(shù)據(jù)的地址賦給指向const的指針(不能通過指向const的指針修改數(shù)據(jù))
//但是不能將const數(shù)據(jù)的地址賦給非const指針
指針、數(shù)組、字符串
int main(){
    int num_text[5]={1,3,5,7,9};
    char char_text[6]={'a','b','c','d','e','\0'};
    int* nt=num_text;char* ct=char_text;
    cout<

對(duì)于數(shù)組與字符串而言,指針名和數(shù)組名都是指向第一個(gè)元素的指針,但是cout字符串指針時(shí),會(huì)打印出整個(gè)字符串

使用 *指針 等效于變量名, *(指針+1)意味著下一個(gè)元素,即text[3] == *(te+3)

new 與 delete
typeName * pointer_name = new typeName
//程序員告訴new要一個(gè)什么類型的內(nèi)存,new就會(huì)找到這樣一個(gè)內(nèi)存塊,并返回它的指針
#includeusing namespace std;
int main(){
    int n;cin>>n;
    int* int_point = new int;
    int* ints_point = new int[n];
    char* char_point = new char;
    char* chars_point = new char[n];//new可以用來創(chuàng)建動(dòng)態(tài)數(shù)組
    delete [] chars_point;
    delete [] ints_point;
    delete int_point;
    delete char_point;
}//new完不要忘記delete

使用new創(chuàng)建動(dòng)態(tài)結(jié)構(gòu)

struct test{
    int num;
    char name;
};

int main(){
    auto* st = new test;
    cin>>st->name;
    cin>>(*st).num;
    cout<<(*st).name<<" "<num;
    return 0;
}
d 3
d 3
int main(){
    auto* st = new test[3];
    cin>>(st+1)->name;
    cin>>(*(st+1)).num;
    cout<<(*(st+1)).name<<" "<<(st+1)->num;
    return 0;
}
d 3
d 3
數(shù)組的替代品 模板類 vector

https://www.runoob.com/w3cnote/cpp-vector-container-analysis.html

引用變量(見函數(shù)探幽部分) 第三章 循環(huán)與關(guān)系表達(dá)式 for 循環(huán) while循環(huán) do…while循環(huán) 第四章 函數(shù)

C++自帶了一個(gè)包含函數(shù)的大型庫(kù)(標(biāo)準(zhǔn)ANSI庫(kù)加上多個(gè)C++類),但真正的編程樂趣在于編寫自己的函數(shù)

構(gòu)建函數(shù)
  1. 提供函數(shù)定義
void functionName(parameterList)//參數(shù)列表,要說清楚參數(shù)類型和參數(shù)名
{
	wuliwala;		
	return xxx;		//可選
}

typeName functionName(parameterList)
{
	wuliwala;
	return xxx;		//必須和typeName一樣
}
  1. 提供函數(shù)原型
void functionName(parameterList);//參數(shù)列表可以只寫參數(shù)類型
typeName functionName(parameterList);//在使用函數(shù)之前聲明(一般放在main()之前)
  1. 調(diào)用函數(shù)
functionName(實(shí)參(用逗號(hào)隔開));
注意

C++對(duì)于返回值的類型有一定的限制:不能是數(shù)組,但可以是其他任何類型(整數(shù)、浮點(diǎn)數(shù)、指針、甚至可以是結(jié)構(gòu)和對(duì)象)

但是,C++可以將數(shù)組作為結(jié)構(gòu)或?qū)ο蟮慕M成部分來返回。另外,C++可以以任何數(shù)據(jù)類型為參數(shù)

void test0(int a){//以變量為參數(shù),函數(shù)體內(nèi)用的是實(shí)參的副本,副本改變不會(huì)影響實(shí)參
    cin>>a;
    cout<>*a;//以指針為參數(shù),通過指針會(huì)影響實(shí)參
    cout<<*a<<"\n";
}

void test2(const int* a){
    //"cin>>*a;"錯(cuò)誤,不能通過const的指針修改指向值
    cout<<*a<<"\n";
}
//const可以保護(hù)指針指向的內(nèi)容,確保它不會(huì)在函數(shù)里被修改,數(shù)組的指針也可以保護(hù),啥都能保護(hù)
int main(){
    int b;
    int* c=&b;
    test1(c);
    cout<
int test0(int* a){
    cin>>a[0];
    cin>>*(a+1);
    return a[0]+1;
}
//指針會(huì)被函數(shù)當(dāng)做數(shù)組名直接用
int test1(int a[]){
    cin>>a[0];
    cin>>*(a+1);
    return a[0]+1;
}
//如果實(shí)際想操作的不是數(shù)組,建議不要用指針,直接用int test(int a){}比較好
int main(){
    int a[3]={1,2,3};
    cout<
int a=80;
const int* text0=&a;//指向 const int(常量)的指針
int* const test1=&a;//指向 int 的 const 的指針(指針本身不能被改變)
//注意,可以將const數(shù)據(jù)或非const數(shù)據(jù)的地址賦給指向const的指針(不能通過指向const的指針修改數(shù)據(jù))
//但是不能將const數(shù)據(jù)的地址賦給非const指針
return 0;
使用數(shù)組區(qū)間的函數(shù)/函數(shù)與字符串
int sum_arr(const int* begin,const int* end){
    const int* pt;
    int total=0;
    for(pt=begin;pt!=end;pt++){
        total = total + *pt;
    }
    return total;
}
int main(){
    int a[5]={1,2,3,4,5};
    cout<
char* builder(char c,int n){
    char* pstr = new char[n+1];
    pstr[n]='\0';
    while (n-->0){
        pstr[n]='c';
    }
    return pstr;
}

int main(){
    char* ps=builder('d',3);
    //delete[] pstr;不能解放pstr,它在函數(shù)里面,應(yīng)該用ps把它接出來,然后解放
    cout<
函數(shù)與結(jié)構(gòu)
struct time{
    int hours;
    int mins;
};
time sum(time m,time n){
    time total={0,0};
    total.hours=m.hours+n.hours;
    total.mins=m.mins+n.mins;
    return total;
}
int main(){
    time m = {3,2};
    time n = {4,6};
    time s = sum(m,n);
    cout<
函數(shù)與二維數(shù)組
int sum(int ar2[][4],int size){
    int total=0;
    for(int r=0;r{1,2,3,4},{9,8,7,6},{2,4,6,8}};
    //data是一個(gè)數(shù)組名,該數(shù)組有三個(gè)元素,每個(gè)元素都是由4個(gè)int組成的數(shù)組
    //也就是說,每個(gè)元素都是一個(gè)指針,指向由4個(gè)int組成的數(shù)組的指針
    //以data為參數(shù)時(shí),應(yīng)該這樣描述:int (*ar2)[4] 或者 int ar2[][4]
    //這時(shí),我們要對(duì)行數(shù)用另一個(gè)參數(shù)加以限制
    int sums=sum(data,2);
    cout<
函數(shù)與string
void display(const string sa[],int n){
    for(int i=0;i
遞歸 含義

第一要素:明確函數(shù)想要干什么

對(duì)于遞歸,我覺得很重要的一個(gè)事就是,這個(gè)函數(shù)的功能是什么,他要完成什么樣的一件事,而這個(gè),是完全由你自己來定義的。也就是說,我們先不管函數(shù)里面的代碼什么,而是要先明白,你這個(gè)函數(shù)是要用來干什么。

例如,我定義了一個(gè)函數(shù)

// 算 n 的階乘(假設(shè)n不為0)
int f(int n){

}

這個(gè)函數(shù)的功能是算 n 的階乘。好了,我們已經(jīng)定義了一個(gè)函數(shù),并且定義了它的功能是什么,接下來我們看第二要素。

第二要素:尋找遞歸結(jié)束條件

所謂遞歸,就是會(huì)在函數(shù)內(nèi)部代碼中,調(diào)用這個(gè)函數(shù)本身,所以,我們必須要找出遞歸的結(jié)束條件,不然的話,會(huì)一直調(diào)用自己,進(jìn)入無底洞。也就是說,我們需要找出當(dāng)參數(shù)為啥時(shí),遞歸結(jié)束,之后直接把結(jié)果返回,請(qǐng)注意,這個(gè)時(shí)候我們必須能根據(jù)這個(gè)參數(shù)的值,能夠直接知道函數(shù)的結(jié)果是什么。

例如,上面那個(gè)例子,當(dāng) n = 1 時(shí),那你應(yīng)該能夠直接知道 f(n) 是啥吧?此時(shí),f(1) = 1。完善我們函數(shù)內(nèi)部的代碼,把第二要素加進(jìn)代碼里面,如下

// 算 n 的階乘(假設(shè)n不為0)
int f(int n){
    if(n == 1){
        return 1;
    }
}

有人可能會(huì)說,當(dāng) n = 2 時(shí),那我們可以直接知道 f(n) 等于多少啊,那我可以把 n = 2 作為遞歸的結(jié)束條件嗎?

當(dāng)然可以,只要你覺得參數(shù)是什么時(shí),你能夠直接知道函數(shù)的結(jié)果,那么你就可以把這個(gè)參數(shù)作為結(jié)束的條件,所以下面這段代碼也是可以的。

// 算 n 的階乘(假設(shè)n>=2)
int f(int n){
    if(n == 2){
        return 2;
    }
}

注意我代碼里面寫的注釋,假設(shè) n >= 2,因?yàn)槿绻?n = 1時(shí),會(huì)被漏掉,當(dāng) n<= 2時(shí),f(n) = n,所以為了更加嚴(yán)謹(jǐn),我們可以寫成這樣:

// 算 n 的階乘(假設(shè)n不為0)
int f(int n){
    if(n<= 2){
        return n;
    }
}

第三要素:找出函數(shù)的等價(jià)關(guān)系式

第三要素就是,我們要不斷縮小參數(shù)的范圍,縮小之后,我們可以通過一些輔助的變量或者操作,使原函數(shù)的結(jié)果不變。

例如,f(n) 這個(gè)范圍比較大,我們可以讓 f(n) = n * f(n-1)。這樣,范圍就由 n 變成了 n-1 了,范圍變小了,并且為了原函數(shù)f(n) 不變,我們需要讓 f(n-1) 乘以 n。

說白了,就是要找到原函數(shù)的一個(gè)等價(jià)關(guān)系式,f(n) 的等價(jià)關(guān)系式為 n * f(n-1),即

f(n) = n * f(n-1)。

找出了這個(gè)等價(jià),繼續(xù)完善我們的代碼,我們把這個(gè)等價(jià)式寫進(jìn)函數(shù)里。如下:

// 算 n 的階乘(假設(shè)n不為0)
int f(int n){
    if(n<= 2){
        return n;
    }
    // 把 f(n) 的等價(jià)操作寫進(jìn)去
    return f(n-1) * n;
}

至此,遞歸三要素已經(jīng)都寫進(jìn)代碼里了,所以這個(gè) f(n) 功能的內(nèi)部代碼我們已經(jīng)寫好了。

例子
//斐波那契數(shù)列的是這樣一個(gè)數(shù)列:1、1、2、3、5、8、13、21、34....,
// 即第一項(xiàng) f(1) = 1,第二項(xiàng) f(2) = 1.....,第 n 項(xiàng)目為 f(n) = f(n-1) + f(n-2)。
// 求第 n 項(xiàng)的值是多少。
int fibo(int n){
    if(n<=2){
        return 1;
    }
    return fibo(n-1)+fibo(n-2);
}
int main(){
    cout<
//一只青蛙一次可以跳上1級(jí)臺(tái)階,也可以跳上2級(jí)。求該青蛙跳上一個(gè)n級(jí)的臺(tái)階總共有多少種跳法。
int gua(int n){
    if(n<=2){
        return n;
    }
    return gua(n-1)+gua(n-2);//第一次跳一級(jí)還剩n-1級(jí),跳兩級(jí)還剩n-2級(jí)
}
int main(){
    cout<
函數(shù)指針
#includedouble betsy(int);
double pam(int);
void estimate(int lines, double (*pf)(int));//傳參時(shí)特征標(biāo)和返回值都應(yīng)匹配
// 第二個(gè)參數(shù)是一個(gè)函數(shù)指針,它指向參數(shù)為int、返回值為double的指針
int main()
{
    using namespace std;
    int code;
    cout<< "How many lines of code do you need? ";
    cin >>code;
    cout<< "Here's Betsy's estimate:\n";
    estimate(code, betsy);
    cout<< "Here's Pam's estimate:\n";
    estimate(code, pam);
    // cin.get();
    // cin.get();
    return 0;
}//函數(shù)指針使得一個(gè)函數(shù)可以調(diào)用不同的函數(shù)
//直接在函數(shù)中調(diào)用函數(shù)則做不到這一點(diǎn) 
double betsy(int lns)
{
    return 0.05 * lns;
}//兩種函數(shù)對(duì)應(yīng)兩種“算法”
//使用函數(shù)指針可以靈活的任意調(diào)用
double pam(int lns)
{
    return 0.03 * lns + 0.0004 * lns * lns;
}
//函數(shù)名就是函數(shù)指針,聲明指針的格式:typeName (*pointName)(argumentType)
void estimate(int lines, double (*pf)(int))
{
    using namespace std;
    cout<< lines<< " lines will take ";
    cout<< (*pf)(lines)<< " hour(s)\n";
}//(*pf)可以是任意滿足類型的函數(shù),而不是確定的函數(shù)

How many lines of code do you need?30
 Here's Betsy's estimate:
30 lines will take 1.5 hour(s)
Here's Pam's estimate:
30 lines will take 1.26 hour(s)
const double* f1(const double ar[],int n);
const double* f2(const double [],int);//返回值為指向常量的指針,參數(shù)列表為常量數(shù)組和int
const double* f3(const double *,int);
//需要注意的是,f1、f2、f3的特征標(biāo)看似不同,但實(shí)際上相同
//接下來聲明一個(gè)指針,它可指向這三個(gè)函數(shù)之一,假設(shè)給指針名為pa
const double* (*pl)(const double*,int);
const double* (*pl)(const double*,int) = f1;//可以在聲明的同時(shí)初始化
auto p2 = f2;//用auto將更加方便
typedef
int main(){
    typedef double real;
    real a=3.5;
    typedef const double* (*p_fun)(const double*,int);
    //*p_fun = f1錯(cuò)誤用法
    p_fun f1;//正確用法
    typedef int* test;
    test b;//正確用法
    //*test b;錯(cuò)誤用法
}
第五章 函數(shù)探幽 內(nèi)聯(lián)函數(shù)

常規(guī)函數(shù)和內(nèi)聯(lián)函數(shù)之間的主要區(qū)別不在于便攜方式,而在于C++編譯器如何將他們組合到程序中

用通俗的語言來講,普通函數(shù)在程序調(diào)用函數(shù)時(shí),會(huì)跳到函數(shù)內(nèi)存塊,再跳回來,

而對(duì)于內(nèi)聯(lián)函數(shù),編譯器將使用相應(yīng)的函數(shù)代碼替換函數(shù)調(diào)用,

內(nèi)聯(lián)函數(shù)的運(yùn)行速度比常規(guī)函數(shù)稍快,但需要占用更多內(nèi)存。

#includeusing namespace std;
//使用inline函數(shù),必須在函數(shù)聲明和函數(shù)定義前加上關(guān)鍵字inline
inline double square(double x)
{
    return x * x;
}//這里直接在main之前定義,就不用再聲明了

int main()
{
    double a, b;
    double c = 13.0;
    a = square(5.0);
    b = square(4.5 + 7.5);   //可以用表達(dá)式做參數(shù)
    cout<< "a = "<< a<< ", b = "<< b<< "\n";
    cout<< "c = "<< c;
    cout<< ", c squared = "<< square(c++)<< "\n";
    cout<< "Now c = "<< c<< "\n";
    return 0;  
}
a = 25, b = 144
c = 13, c squared = 169
Now c = 14
引用變量

C++新增了一種復(fù)合類型——引用變量,引用是已定義的變量的別名,它最主要的用途是用作函數(shù)的形參,

通過將引用變量作為參數(shù),函數(shù)將使用原始數(shù)據(jù),而不是其副本。

//C和C++使用&來指示變量的地址,C++給&符號(hào)賦予了另一個(gè)含義
int rats;
int& rodents = rats;//使rodents成為rats的另一個(gè)名字
//在上句中,&不是地址運(yùn)算符,而是類型標(biāo)識(shí)符的一部分
//就像char*表示指向char的指針一樣,int&是指向int的引用
//上述引用聲明允許將rats和rodents互換,它們指向相同的值和內(nèi)存單元
int rats = 101;
int& rodents = rats;
int* prats = &rats;
//引用看上去很像(*指針),但實(shí)際上,引用還是不同于指針的,除了表示方法不同外,還有其他差別
//比如,引用不能像指針那樣,先聲明、再賦值,必須在聲明引用變量時(shí)同時(shí)初始化
//引用更接近c(diǎn)onst指針,必須在創(chuàng)建時(shí)初始化,一旦與某個(gè)變量關(guān)聯(lián)起來,就將一直效忠于它。
int& rodents = rats;
int* const pr = &rats;
//二者類似,其中引用rodents扮演的角色與表達(dá)式*pr相同
//引用變量的值一旦確定,就不會(huì)更改
int rats = 101;
int* pt = &rats;
int& rodents = *pt;//rodents在此處初始化
int bunnies = 50;
pt = &bunnies;//將pt改變,而rodents似乎會(huì)隨著pt改變(畢竟初始化的時(shí)候,rodents是*pt的別名)
//但是,rodents并不會(huì)被改變,因?yàn)樗е业挠肋h(yuǎn)是那一刻的*pt,也就是rats
//哪怕*pt在未來變了,它也不會(huì)改變
將引用作為函數(shù)參數(shù)

引用經(jīng)常被用作函數(shù)參數(shù),使得函數(shù)中的變量名成為調(diào)用程序中的變量的別名

這種做法并沒有繞過引用不能先定義再賦值的設(shè)定,因?yàn)楹瘮?shù)直到被調(diào)用,它里面的變量才會(huì)被定義

void swapr(int& a, int& b)
{
    int temp;
    temp = a;	a = b;	b = temp; 
}
void swapp(int* p, int* q)
{
    int temp;
    temp = *p;	*p = *q;  *q = temp;
}
void swapv(int a, int b)
{
    int temp;
    temp = a;	a = b;	b = temp;
}
int main()
{
    using namespace std;
    int wallet1 = 300;	int wallet2 = 350;
    
    cout<< "wallet1 = $"<< wallet1<< " wallet2 = $"<< wallet2<< endl;

    cout<< "Using references to swap contents:\n";
    swapr(wallet1, wallet2);//參數(shù)傳遞實(shí)現(xiàn)了int& a=wallet1 和 int& b=wallet2
    cout<< "wallet1 = $"<< wallet1<< " wallet2 = $"<< wallet2<< endl;

    cout<< "Using pointers to swap contents again:\n";
    swapp(&wallet1, &wallet2);//參數(shù)傳遞實(shí)現(xiàn)了int* a=&wallet1 和 int* b=&wallet2
    cout<< "wallet1 = $"<< wallet1<< " wallet2 = $"<< wallet2<< endl;

    cout<< "Trying to use passing by value:\n";
    swapv(wallet1, wallet2);//參數(shù)傳遞實(shí)現(xiàn)了int a=wallet1 和 int b=wallet2
    cout<< "wallet1 = $"<< wallet1<< " wallet2 = $"<< wallet2<< endl;
    return 0;
}

wallet1 = $300 wallet2 = $350
Using references to swap contents:
wallet1 = $350 wallet2 = $300//使用引用互換,成功
Using pointers to swap contents again:
wallet1 = $300 wallet2 = $350//使用指針互換,成功(又換回來了)
Trying to use passing by value:
wallet1 = $300 wallet2 = $350//使用副本互換(值傳遞),失?。]變)
注意事項(xiàng)

避免返回函數(shù)終止時(shí)不再存在的內(nèi)存單元引用

const int& clone(int& ft)//本代碼塊為錯(cuò)誤示范
{
    int a;
    a=ft;
    return a;//把ft的值給了a,并且返回a
}//該函數(shù)返回了一個(gè)臨時(shí)變量的引用
const string& version3(string& s1,const string& s2)
{
    string temp;
    temp = s2+s1+s2;
    return temp;//返回了臨時(shí)變量的引用
}
string version1(const string & s1, const string & s2)
{
    string temp;
    temp = s2 + s1 + s2;
    return temp;//返回類型為string,temp將被復(fù)制到一個(gè)臨時(shí)存儲(chǔ)單元
}

const string & version2(string & s1, const string & s2)   // has side effect
{
    s1 = s2 + s1 + s2;
    return s1; //返回了已有的引用
}

const string & version3(string & s1, const string & s2)   // bad design
{
    string temp;
    temp = s2 + s1 + s2;
    return temp;//返回了臨時(shí)變量的引用,錯(cuò)誤示范
}
 
int main()
{
    string input;
    string copy;
    string result;

    cout<< "Enter a string: ";
    getline(cin, input);
    copy = input;
    cout<< "Your string as entered: "<< input<< endl;
    result = version1(input, "***");
    cout<< "Your string enhanced: "<< result<< endl;
    cout<< "Your original string: "<< input<< endl;
 
    result = version2(input, "###");
    cout<< "Your string enhanced: "<< result<< endl;
    cout<< "Your original string: "<< input<< endl;

    cout<< "Resetting original string.\n";
    input = copy;
    result = version3(input, "@@@");
    cout<< "Your string enhanced: "<< result<< endl;
    cout<< "Your original string: "<< input<< endl;
    return 0;
}
Enter a string:sdfaf
Your string as entered: sdfaf
Your string enhanced: ***sdfaf***
Your original string: sdfaf
Your string enhanced: ###sdfaf###
Your original string: ###sdfaf###//version2直接用了input的引用,導(dǎo)致input也隨之變化
Resetting original string.

Process finished with exit code -1073741819 (0xC0000005)//version3報(bào)錯(cuò)

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧

網(wǎng)站欄目:C++學(xué)習(xí)筆記(更新)-創(chuàng)新互聯(lián)
當(dāng)前URL:http://muchs.cn/article20/cdidjo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供軟件開發(fā)、網(wǎng)站維護(hù)企業(yè)網(wǎng)站制作、商城網(wǎng)站、營(yíng)銷型網(wǎng)站建設(shè)手機(jī)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)