C++對C語言的擴充套件(1)--引用
C++對C語言的擴充套件--引用
1 引用
1.1 變數名
- 變數名實質上是一段連續儲存空間的別名,是一個標號
- 通過變數名來申請並命名記憶體空間
- 通過變數的名字可以使用記憶體空間
1.2 引用的概念
-
變數名,本身是一段記憶體的引用,即別名(alias)。引用可以看作一個已定義變數的別名。
-
引用的語法:Type &name = var;
用法如下:
int a = 10; // 編譯器分配4個位元組的記憶體空間,a為此記憶體空間的別名,可以通過a來修改記憶體空間的值
int &b = a; // b為a的引用,即b為a的別名,同樣可以通過b來修改記憶體空間的值
1.3 規則
- 引用沒有定義,是一種關係型宣告。宣告它和原有某一變數(實體)的關係。故而型別與原型別保持一致,且不分配記憶體。與被引用的變數有相同的地址。
- 宣告的時候必須初始化,一經宣告,不可變更。
- 可對引用,再次引用。多次引用的結果,是某一變數具有多個別名。
- &符號前有資料型別時,是引用。其它皆為取地址。
int a, b;
int &r = a; // 正確,變數與引⽤用具有相通的地址
int &r = b; // 錯誤,不可以修改原有的引用關係
float &rr = b; // 錯誤,引用型別不匹配
int &ra = r; // 正確,可以對引用再次引用,此時a有兩個別名r和ra
1.4 引用作為函式引數
普通引用在宣告時必須用其它的變數進行初始化,引用作為函式引數宣告時不進行初始化。
void test01(int &i) // 形參為引用,相當於對傳入形參起別名
{
r = 100; // 相當於修改了主函式中a為100
return;
}
void test02(int j)
{
j = 1000;
}
int main()
{
int a = 10;
cout << a << endl; // a = 10
test01(a); // 當傳a進去時,i為a的別名,i與a為相同的記憶體地址
cout << a << endl; // a = 100;
test02(a);
cout << a << endl; // a = 100;
}
1.5 引用的意義
- 引用作為其它變數的別名而存在,因此在一些場合可以代替指標
- 引用相對於指標來說具有更好的可讀性和實用性
1.6 引用的本質
- 引用在C++中的內部實現是一個常指標
Type& name <===> Type const name*
- C++編譯器在編譯過程中使用常指標作為引用的內部實現,因此引用所佔用的空間大小與指標相同
- 從使用的角度,引用會讓人誤會其只是一個別名,沒有自己的儲存空間。這是C++為了實用性而做出的細節隱藏。
間接賦值的3各必要條件
- 定義兩個變數 (一個實參一個形參)
- 建立關聯 實參取地址傳給形參
- *p形參去間接的修改實參的值
引用在實現上,只不過是把:間接賦值成立的三個條件的後兩步和二為一.
當實參傳給形參引用的時候,只不過是c++編譯器幫我們程式設計師手工取了
一個實參地址,傳給了形參引用(常量指標) 。
1.7 引用作為函式的返回值
1.7.1 若返回棧變數的引用
不能成為其它引用的初始值(不能作為左值使用)
儘量不要返回棧變數作引用
#include <iostream>
using namespace std;
int getA1()
{
int a;
a = 10;
return a;
}
int& getA2()
{
int a;
a = 10;
return a;
}
int main()
{
int a1 = 0;
int a2 = 0;
// 值拷貝
a1 = getA1();
// 將一個引用賦給一個變數,會有拷貝動作
// 理解:編譯器類似做了如下隱藏操作,a2 = *(getA2());
a2 = getA2();
// 將一個引用賦給另一個引用作為初始值,由於是棧記憶體的引用(函式getA2中變數a為棧記憶體,出了作用域就棧記憶體就釋放了),記憶體非法
//int &a3 = getA2();
cout << "a1 = " << a1 << endl;
cout << "a2 = " << a2 << endl;
// cout << "a3 = " << a3 << endl;
return 0;
}
1.7.2 若返回靜態變數或全域性變數引用
可以成為其他引用的初始值(可作為右值使用,也可作為左值使用)
#include <iostream>
using namespace std;
int getA1()
{
int a;
static a = 10;
return a;
}
int& getA2()
{
int a;
static a = 10;
return a;
}
int main()
{
int a1 = 0;
int a2 = 0;
// 值拷貝
a1 = getA1();
// 將一個引用賦給一個變數,會有拷貝動作
// 理解:編譯器類似做了如下隱藏操作,a2 = *(getA2());
a2 = getA2();
// 將一個引用賦給另一個引用作為初始值,由於是靜態區域,記憶體合法
int &a3 = getA2();
cout << "a1 = " << a1 << endl;
cout << "a2 = " << a2 << endl;
cout << "a3 = " << a3 << endl;
return 0;
}
- 如果返回值為引用可以當左值。(返回變數本身)(全域性/靜態變數)
- 如果返回值為普通變數不可以當左值。(返回變數的值)
#include <iostream>
using namespace std;
// 函式當左值
// 返回變數的值
int func1()
{
int a;
static a = 10;
return a;
}
// 返回變數本身
int& func2()
{
int a;
static a = 10;
return a;
}
int main()
{
int a1 = 0;
int a2 = 0;
// 函式當右值
a1 = func1();
cout << "a1 = " << a1 << endl; // a1 = 10
// 函式返回值是一個引用,並且當右值
a2 = func2();
cout << "a2 = " << a2 << endl; // a2 = 10
// 函式當左值
//func1 = 100; // 錯誤
func2 = 100; // 函式返回值是⼀一個引⽤用,並且當左值
int a3 = func2();
cout << "a3 = " << a3 << endl; // a3 = 100
return 0;
}
1.8 陣列的引用
//1.直接建立引用
int arr[10];
int(&pArr)[10] = arr;
// 使用
pArr[0] = 1; // 相當於arr[0] = 1;
----------------------------------------------------------------
//2.先定義出陣列型別,再通過型別 定義引用
int arr[10];
typedef int(ARRAY_TYPE)[10]; // 建立出10個int型別的陣列,叫ARRAY_TYPE,即ARRAY_TYPE為10個int型別的陣列
ARRAY_TYPE & pArr2 = arr;
// 使用
pArr2[0] = 1; // 相當於arr[0] = 1;
1.9 指標的引用
#include <iostream>
using namespace std;
struct Person
{
string name;
int age;
};
void getPerson01(Person **p)
{
(*p)->name = "張三";
(*p)->age = 18;
return;
}
void getPerson02(Person *&p)
{
p->name = "李四";
p->age = 20;
return;
}
int main()
{
Person *pP = new Person;
//1 c語⾔言中的⼆二級指標
getPerson01(&pP);
cout << "姓名:" << pP->name << " 年齡:" << pP->age << endl;
//2 c++中的引⽤用 (指標的引⽤用)
//引⽤用的本質 間接賦值後2個條件 讓c++編譯器幫我們程式設計師做了
getPerson02(pP);
cout << "姓名:" << pP->name << " 年齡:" << pP->age << endl;
delete pP;
return 0;
}
利用引用可以簡化指標
可以直接用同級指標的引用,給同級指標分配空間
1.10 const 引用
const 引用有較多使用。它可以防止物件的值被隨意修改。因而具有一些特性。
1.const 物件的引用必須是const的,將普通引用繫結到const物件是不合法的。
const int a =1; // const變數
int &b = a; // 普通引用b繫結到const物件不合法
------------------------------------------------------------
int x = 1;
const int &y = a; // 合法,常引⽤用是限制變數為只讀 不能通過y去修改x了
// y = 2; // 錯誤
- const 引用可使用相關型別的物件(常量,非同型別的變數或表示式)初始化。這個是const 引用與普通引用最大的區別。
const int a =1; // const變數
const int &b = a; // 合法
------------------------------------------------------------
double x = 3.14;
const int &y = x; // 合法 y = 3;
1.11 常量的引用
const int &ref = 10;
// 加了const之後,相當於寫成 int temp = 10; const int &ref = temp;
//常量引用的使用場景:修飾函式中的形參,防止誤操作
void test(const int &a)
{
a = 10; // 錯誤
}
1.11 const 引用的原理
const 引用的目的是,禁止通過修改引用值來改變被引用的物件。
const 引用的初始化特性較為微妙,可通過如下程式碼說明:
double val = 3.14;
const int &ref = val; // 使用相關型別的物件初始 相當於 int temp = val; const int &ref = temp;
double &ref2 = val;
cout << ref << " " << ref2 << endl;
val = 4.14;
cout << ref << " " << ref2 << endl;
上述輸出結果為3 3.14和3 4.14。因為ref是const的,在初始化的過程中已經給定值,不允許修改.而被引用的物件是val,是非const的,所以val的修改並未影響ref的值,而ref2的值發生了相應的改變。
為什麼非const的引用不能使用相關型別(常量,非同型別的變數或表示式)初始化呢???
實際上,const 引用使用相關型別物件初始化時發生瞭如下過程:int temp = val; const int &ref = temp;
如果 ref 不是const的,那麼改變ref 值,修改的是 temp,而不是 val。期望對ref的賦值會修改val的程式設計師會發現val實際並未修改。
因此不允許使用相關型別初始化非const引用
// 1.⽤用變數 初始化 常引⽤用
int x1 = 30;
const int &y1 = x1; // 用x1變數初始化 常引用
// 2.用字面量初始 常量引用
const int a = 40; // c++編譯器把a放在符號表中
// int &m = 41; // 錯誤
const int &m = 42; // c++ 會分配記憶體空間
// 相當於 int temp = 42; const int &m = temp;
1.12 引用的注意事項
- 引用必須引一塊合法記憶體空間
- 不要返回區域性變數的引用
- 當函式返回值是引用時候,那麼函式的呼叫可以作為左值進行運算
結論:
- const int & e 相當於 const int * const e
- 普通引用 相當於 int *const e
- 當使用常量(字面量)對const引用進行初始化時,C++編譯器會為常量值分配空間,並將引用名作為這段空間的別名
- 使用字面量對const引用初始化後,將生成一個只讀變數
相關文章
- C++ 對C的擴充套件有哪些C++套件
- C++學習筆記-C++對C語言的函式擴充C++筆記C語言函式
- Python擴充套件C/C++ 實現原理分析Python套件C++
- Cython,一個簡化 Python 編寫 C 擴充套件的語言Python套件
- c 語言除錯方法(除錯 PHP 底層、擴充套件)除錯PHP套件
- 基於C++和Rust兩種方式擴充套件nodejs對比C++Rust套件NodeJS
- C++類的完美單元測試方案——基於C++11擴充套件的friend語法C++套件
- Cilium 1.3:支援Envoy、Cassandra和Memcached的Go語言擴充套件Go套件
- C 擴充套件庫 – mysql API套件MySqlAPI
- C++ 開發 PHP 7 擴充套件之原生常量定義C++PHP套件
- kotlin 擴充套件(擴充套件函式和擴充套件屬性)Kotlin套件函式
- C#中的擴充套件類的理解C#套件
- c盤擴充套件卷選項是灰的怎麼辦 c盤不能擴充套件卷的辦法套件
- C-如何快速生成Python的C擴充套件.mdPython套件
- 聊聊C語言/C++—程式和程式語言C語言C++
- MySQL中InnoDB引擎對索引的擴充套件MySql索引套件
- Windows 7 下用C++為node.js寫擴充套件模組WindowsC++Node.js套件
- Python和Java、PHP、C、C#、C++等其他語言的對比?PythonJavaPHPC#C++
- C++和c語言的分別C++C語言
- C/C++語言的學習方向C++
- realloc 實現隨使用者輸入自動擴充套件陣列長度(C語言)套件陣列C語言
- 【Kotlin】擴充套件屬性、擴充套件函式Kotlin套件函式
- C語言/C++對程式設計學習的重要性!C語言C++程式設計
- 細學C++之C++語言的特點C++
- c# ExpandoObject動態擴充套件物件C#Object套件物件
- ?用Chrome擴充套件管理器, 管理你的擴充套件Chrome套件
- Java 中模擬 C# 的擴充套件方法JavaC#套件
- .NET: 談談C#中的擴充套件方法C#套件
- C++ 引用C++
- 【C++】引用C++
- C++ 的指令碼語言:ChaiScriptC++指令碼AI
- PHP擴充套件開發就是一個自己的PHP擴充套件PHP套件
- 擴充套件工具套件
- Sanic 擴充套件套件
- Mybatis擴充套件MyBatis套件
- SpringMVC 擴充套件SpringMVC套件
- ORACLE 擴充套件Oracle套件
- 正則的擴充套件套件