C++學習筆記(更新)-創(chuàng)新互聯

前言

本篇文章是個人的C++學習記錄,也算是《C++ Primer Plus》的讀書筆記,如有錯誤,期望指正

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

在c++中,類是一種規(guī)范,描述了一種新型數據格式,對象是根據這種規(guī)范構造的特定數據結構

也就是說,類是抽象的,描述了有什么數據?對數據有哪些操作?而對象是具體的,是類的實例化

OOP(面向對象編程)并不僅僅是將數據和方法合并成類定義。

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

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

以上是對C++一些相較于C而言比較“新”的東西的總結,也是作者接下來要重點學習的地方

第二章 C++ 內置數據類型 基本類型

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

浮點型變量:float、double、long double

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

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

C++算術運算符

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

對于除法,分別有int類型、long類型、double類型、float類型

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

數組、C風格字符串、string類字符串、結構體、共用體、枚舉、指針、vector類、array類……

數組

聲明數組,需要指出:儲存在每個元素中的值的類型、數組名、數組中的元素數

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

初始化數組,只有在定義數字時才能使用初始化,以后就不能使用了,也不能把一個數組賦給另一個數組,但可以用下標給元素賦值

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

數組賦值

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

C風格字符串的本質是一個以空字符’\0’為結尾的char數組,將字符串存儲到數組中,有兩種方式

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

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

cout<<"sfsagsg""sagsagasg";cout<<"sagasghsahas"	"asfagasgsa";
cout<<"safsagasdhrwjnf"
"sagasghshdahna";
  1. 將鍵盤或文件輸入讀入到數組中
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);//遇見換行符或者到達大值(算上換行)停止,吃掉換行符,并把它變成空字符
cin.get(dog,20);//遇見換行符或者到達大值(算上換行)停止,不吃換行符,把它留在輸入流中
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對象,可以調用get()
string類
#include#includeusing namespace std;
int main(){//string對象在很多方面與字符數組相同,初始化方式可以一樣,可以cin,可以cout,也可以用數組表示法訪問單個字符
    string str1;string str2="asdfg";
    str1="sdafasf";str2="asdfgg";
    cout<
運算符重載
  1. + 和 +=:連接字符串
  2. =:字符串賦值
  3. >、>=、< 和<=:字符串比較(例如a< b, aa< ab)
  4. ==、!=:比較字符串
  5. <<、>>:輸出、輸入字符串

注意:使用重載的運算符 + 時,必須保證前兩個操作數至少有一個為 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] 范圍內查找并返回字符串 ab 在 str 的位置
str.rfind("ab", 2);//在 str[0]~str[2] 范圍內查找并返回字符串 ab 在 str 的位置

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

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

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

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

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

查找給定字符串并把相應子串替換為另一給定字符串

string 并沒有提供這樣的函數,所以我們自己來實現。由于給定字符串可能出現多次,所以需要用到find()成員函數的第二個參數,每次查找之后,從找到位置往后繼續(xù)搜索。直接看代碼(這個函數返回替換的次數,如果返回值是 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;
}

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

方法和上面相似,內部使用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;
}
結構體

數組可以將多個同類型元素存儲在一起,而結構體可以將多種類型的數據存儲在一起

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

共用體可以存儲不同的數據類型,但只能存儲其中的一種類型

union one4all{
    int int_val;
    long long_val;
    double double_val;
}pail;//可以用pail存儲int、long、double中的一種,條件是在不同的時間進行。

共用體與結構體共用

struct wight{
    char brand[20];int type;
    union id{
        long id_num;
        char id_char[20];
    }id_val;//id_val為組成部分,調用時要通過id_val,id_num,id_char為id_val成員,一個有值另一個就會沒值
};
struct other{
    char brand[20];int type;
    union {
        long id_num;
        char id_char[20];
    };//匿名共用體,id_num,id_char為結構體成員,一個有值另一個就會沒值
};//匿名匿的是共用體和共用體變量的名,不單單是共用體變量的名
int main(){
    wight a={};other b={};
    cin>>a.id_val.id_char;
    cin>>b.id_num;
    return 0;//這里共用的作用是,可以根據type判斷id是那種類型,這樣就減少了空間占用。
}
枚舉
enum spectrum {red,orange,yellow,green,blue,violet,indigo,ultraviolet};
//讓spectrum成為新類型的名稱,它被稱為枚舉,花括號里的東西變成符號常量
spectrum a;//則a只能用花括號里的東西賦值
enum bits {one=1,two=2,three=3};
enum {on=1,tw=2,thre};//枚舉相當于多個const,但是值只能是整數
指針和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數據或非const數據的地址賦給指向const的指針(不能通過指向const的指針修改數據)
//但是不能將const數據的地址賦給非const指針
指針、數組、字符串
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<

對于數組與字符串而言,指針名和數組名都是指向第一個元素的指針,但是cout字符串指針時,會打印出整個字符串

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

new 與 delete
typeName * pointer_name = new typeName
//程序員告訴new要一個什么類型的內存,new就會找到這樣一個內存塊,并返回它的指針
#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)建動態(tài)數組
    delete [] chars_point;
    delete [] ints_point;
    delete int_point;
    delete char_point;
}//new完不要忘記delete

使用new創(chuàng)建動態(tài)結構

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
數組的替代品 模板類 vector

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

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

C++自帶了一個包含函數的大型庫(標準ANSI庫加上多個C++類),但真正的編程樂趣在于編寫自己的函數

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

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

C++對于返回值的類型有一定的限制:不能是數組,但可以是其他任何類型(整數、浮點數、指針、甚至可以是結構和對象)

但是,C++可以將數組作為結構或對象的組成部分來返回。另外,C++可以以任何數據類型為參數

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

void test2(const int* a){
    //"cin>>*a;"錯誤,不能通過const的指針修改指向值
    cout<<*a<<"\n";
}
//const可以保護指針指向的內容,確保它不會在函數里被修改,數組的指針也可以保護,啥都能保護
int main(){
    int b;
    int* c=&b;
    test1(c);
    cout<
int test0(int* a){
    cin>>a[0];
    cin>>*(a+1);
    return a[0]+1;
}
//指針會被函數當做數組名直接用
int test1(int a[]){
    cin>>a[0];
    cin>>*(a+1);
    return a[0]+1;
}
//如果實際想操作的不是數組,建議不要用指針,直接用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數據或非const數據的地址賦給指向const的指針(不能通過指向const的指針修改數據)
//但是不能將const數據的地址賦給非const指針
return 0;
使用數組區(qū)間的函數/函數與字符串
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,它在函數里面,應該用ps把它接出來,然后解放
    cout<
函數與結構
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<
函數與二維數組
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是一個數組名,該數組有三個元素,每個元素都是由4個int組成的數組
    //也就是說,每個元素都是一個指針,指向由4個int組成的數組的指針
    //以data為參數時,應該這樣描述:int (*ar2)[4] 或者 int ar2[][4]
    //這時,我們要對行數用另一個參數加以限制
    int sums=sum(data,2);
    cout<
函數與string
void display(const string sa[],int n){
    for(int i=0;i
遞歸 含義

第一要素:明確函數想要干什么

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

例如,我定義了一個函數

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

}

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

第二要素:尋找遞歸結束條件

所謂遞歸,就是會在函數內部代碼中,調用這個函數本身,所以,我們必須要找出遞歸的結束條件,不然的話,會一直調用自己,進入無底洞。也就是說,我們需要找出當參數為啥時,遞歸結束,之后直接把結果返回,請注意,這個時候我們必須能根據這個參數的值,能夠直接知道函數的結果是什么。

例如,上面那個例子,當 n = 1 時,那你應該能夠直接知道 f(n) 是啥吧?此時,f(1) = 1。完善我們函數內部的代碼,把第二要素加進代碼里面,如下

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

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

當然可以,只要你覺得參數是什么時,你能夠直接知道函數的結果,那么你就可以把這個參數作為結束的條件,所以下面這段代碼也是可以的。

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

注意我代碼里面寫的注釋,假設 n >= 2,因為如果 n = 1時,會被漏掉,當 n<= 2時,f(n) = n,所以為了更加嚴謹,我們可以寫成這樣:

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

第三要素:找出函數的等價關系式

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

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

說白了,就是要找到原函數的一個等價關系式,f(n) 的等價關系式為 n * f(n-1),即

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

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

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

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

例子
//斐波那契數列的是這樣一個數列:1、1、2、3、5、8、13、21、34....,
// 即第一項 f(1) = 1,第二項 f(2) = 1.....,第 n 項目為 f(n) = f(n-1) + f(n-2)。
// 求第 n 項的值是多少。
int fibo(int n){
    if(n<=2){
        return 1;
    }
    return fibo(n-1)+fibo(n-2);
}
int main(){
    cout<
//一只青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法。
int gua(int n){
    if(n<=2){
        return n;
    }
    return gua(n-1)+gua(n-2);//第一次跳一級還剩n-1級,跳兩級還剩n-2級
}
int main(){
    cout<
函數指針
#includedouble betsy(int);
double pam(int);
void estimate(int lines, double (*pf)(int));//傳參時特征標和返回值都應匹配
// 第二個參數是一個函數指針,它指向參數為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;
}//函數指針使得一個函數可以調用不同的函數
//直接在函數中調用函數則做不到這一點 
double betsy(int lns)
{
    return 0.05 * lns;
}//兩種函數對應兩種“算法”
//使用函數指針可以靈活的任意調用
double pam(int lns)
{
    return 0.03 * lns + 0.0004 * lns * lns;
}
//函數名就是函數指針,聲明指針的格式: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)可以是任意滿足類型的函數,而不是確定的函數

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);//返回值為指向常量的指針,參數列表為常量數組和int
const double* f3(const double *,int);
//需要注意的是,f1、f2、f3的特征標看似不同,但實際上相同
//接下來聲明一個指針,它可指向這三個函數之一,假設給指針名為pa
const double* (*pl)(const double*,int);
const double* (*pl)(const double*,int) = f1;//可以在聲明的同時初始化
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錯誤用法
    p_fun f1;//正確用法
    typedef int* test;
    test b;//正確用法
    //*test b;錯誤用法
}
第五章 函數探幽 內聯函數

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

用通俗的語言來講,普通函數在程序調用函數時,會跳到函數內存塊,再跳回來,

而對于內聯函數,編譯器將使用相應的函數代碼替換函數調用,

內聯函數的運行速度比常規(guī)函數稍快,但需要占用更多內存。

#includeusing namespace std;
//使用inline函數,必須在函數聲明和函數定義前加上關鍵字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);   //可以用表達式做參數
    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++新增了一種復合類型——引用變量,引用是已定義的變量的別名,它最主要的用途是用作函數的形參,

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

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

引用經常被用作函數參數,使得函數中的變量名成為調用程序中的變量的別名

這種做法并沒有繞過引用不能先定義再賦值的設定,因為函數直到被調用,它里面的變量才會被定義

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);//參數傳遞實現了int& a=wallet1 和 int& b=wallet2
    cout<< "wallet1 = $"<< wallet1<< " wallet2 = $"<< wallet2<< endl;

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

    cout<< "Trying to use passing by value:\n";
    swapv(wallet1, wallet2);//參數傳遞實現了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//使用副本互換(值傳遞),失?。]變)
注意事項

避免返回函數終止時不再存在的內存單元引用

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

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;//返回了臨時變量的引用,錯誤示范
}
 
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的引用,導致input也隨之變化
Resetting original string.

Process finished with exit code -1073741819 (0xC0000005)//version3報錯

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

網站欄目:C++學習筆記(更新)-創(chuàng)新互聯
當前URL:http://bm7419.com/article20/cdidjo.html

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

廣告

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