欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++?引用與內(nèi)聯(lián)函數(shù)詳情

 更新時(shí)間:2022年05月12日 16:46:34   作者:玄鳥軒墨  
這篇文章主要介紹了C++?引用與內(nèi)聯(lián)函數(shù)詳情,主要分享一下關(guān)于引用的知識(shí)點(diǎn),這里都是一些比較基礎(chǔ)的知識(shí),適合初學(xué)者,下文續(xù)航徐介紹需要的小伙伴可以參考一下

引用初階

引用是C++的特性的之一,不過C++沒有沒有給引用特意出一個(gè)關(guān)鍵字,使用了操作符的重載。引用在C++中很常見,常見就意味著它很重要。我們分兩個(gè)境界來談?wù)勔?初階是我們能在書上看到的.

什么是引用

引用不是新定義一個(gè)變量,而是給已存在變量取了一個(gè)別名,編譯器不會(huì)為引用變量開辟內(nèi)存空間,它和它引用的變量共用同一塊內(nèi)存空間 .所謂的取別名,就是取外號(hào),張三在朋友面前可能叫三子,在長輩面前可能叫三兒,但是無論是叫三子還是三兒,他們叫的就是張三,這是無可否認(rèn)的.引用的符號(hào)&和我們?nèi)〉刂返?amp;操作符一樣的,后面我們就會(huì)知道這是操作符構(gòu)成重載的原因.

我們可以理解所謂的引用.

#include <stdio.h>
int main()
{
	int a = 10;
	int& b = a;       //  b 是 a的一個(gè)別名
	printf("%p\n", &a);
	printf("%p\n", &b);
	return 0;
}

為何要有引用

引用有很多優(yōu)點(diǎn),其中有一個(gè)就是可以簡化代碼,我們在C語言中寫過如何交換兩個(gè)整型數(shù)據(jù),需要借助一維指針,但是這有一點(diǎn)麻煩,使用引用就可以很好的解決這個(gè)問題.

#include <stdio.h>
void swap(int& a, int& b)
{
	int c = a;
	a = b;
	b = c;
}
int main()
{
	int a = 10;
	int b = 20;
	printf("交換前  a = %d b = %d\n", a, b);
	swap(a, b);
	printf("交換后  a = %d b = %d\n", a, b);
	return 0;
}

引用指向同一塊空間

我們無論給變量取多少外號(hào),但是這些外號(hào)所指向的空間是一樣的,我們只要修改一個(gè)外號(hào)的空間,就會(huì)導(dǎo)致其他外號(hào)值得修改.

#include <iostream>
using namespace std;
int main()
{
	int a = 10;
	int& b = a;  //b 是  a  的別名
	int& c = b;  //還可以對c取別名
	return 0;
}

int main()
{
	int a = 10;
	int& b = a;  //b 是  a  的別名
	int& c = b;  //還可以對c取別名
	c = 20;
	cout << "a: " << a << endl;
	cout << "b: " << a << endl;
	cout << "c: " << a << endl;
	return 0;
}

引用的特性

即使引用很好使用,但是我們還要關(guān)注他們的一些特性.

定義時(shí)必須初識(shí)化

這個(gè)要求很嚴(yán)格,我們必須在引用的時(shí)候給他初始化,否則就會(huì)報(bào)錯(cuò).

#include <iostream>
using namespace std;
int main()
{
	int a = 10;
	int& b;
	return 0;
}

一個(gè)變量可以多次引用

這個(gè)特性我們前面和大家分享過了,我們可以對一個(gè)變量多次取外號(hào),每一個(gè)外號(hào)都是是這個(gè)變量的別名,我們甚至可以對外號(hào)取外號(hào),不過我們一般不這么干.

引用一旦引用了一個(gè)實(shí)例,不能在再引用其他的實(shí)例

這個(gè)特性完美的闡釋了引用的專一性,一旦我們給某一個(gè)變量起了一個(gè)別名,這個(gè)別名就會(huì)跟著這個(gè)變量一輩子,絕對不會(huì)在成為其他的變量的別名.這個(gè)也是和指針很大的區(qū)別,指針比較花心.

這個(gè)我們就不通過代碼打印各個(gè)變量的地址了.,我們觀看另外一種現(xiàn)象,通過觀察我們看到a的值也被修改了,可以確定 b 仍舊a的別名.

int main()
{
	int a = 10;
	int& b = a;
	int c = 20;
	// 這個(gè)一定是賦值
	b = c;
	cout << "a: " << a << endl;
	return 0;
}

引用進(jìn)階

我們已經(jīng)看到引用的優(yōu)點(diǎn),但是這是引用的基礎(chǔ)用法,要是到那里,我們一般看書都可以做到,現(xiàn)在要看看引用更加詳細(xì)的知識(shí).

常引用

我們在C語言中,專門分享過const相關(guān)的知識(shí),那么我們?nèi)绾螌onst修飾的變量取別名呢?這是一個(gè)小問題.

const int a = 10;

我們看看下面的方法.

int main()
{
	const int a = 10;
	int& b = a;        //報(bào)錯(cuò)
	const int& c = a;  // 不報(bào)錯(cuò)
	return 0;
}

到這里我們就可以發(fā)現(xiàn),我們?nèi)e名的時(shí)候也用const修飾就可以了,這是我們從現(xiàn)象中的得出的結(jié)論,但是這又是因?yàn)槭裁茨?我們需要知道他們的原理.

權(quán)限

不知道大家有沒有在學(xué)習(xí)中看到過這樣一種現(xiàn)象,對于一個(gè)文件,我們可能存在僅僅閱讀的權(quán)限,自己無法修改,但是其他的人有可能有資格修改,這就是權(quán)限的能力,const修飾變量后,使得變量的加密程度更加高了,我們?nèi)e名的時(shí)候,總不能把這個(gè)權(quán)限給過擴(kuò)大了,編譯器這是不允許的,也就是說我們?nèi)e名的時(shí)候,權(quán)限只能往低了走,絕對不能比原來的高.

下面就是把權(quán)限往縮小了:

int main()
{
    int a = 10;
	const int& b = a;
	return 0;
}

常量取別名必須要是const修飾的,因?yàn)槌A坎荒苄薷?

int main()
{
	const int& a = 10;
	return 0;
}

臨時(shí)變量具有常屬性

這個(gè)是語法的一個(gè)知識(shí)點(diǎn),我們記住就可以了,現(xiàn)在我們就要看看為什么這個(gè)代碼會(huì)可以運(yùn)行.

我們常引用 還有最后一個(gè)問題,如果我們是double類型的變量,如何給取一個(gè)int類型的別名?

需要用const修飾,而且會(huì)發(fā)生截?cái)啵?/strong>

int main()
{
	double d = 1.2;
	const int& a = d;       //需要用const修飾
	cout << a << endl;
	return 0;
}

它的本質(zhì)不是取別名,而類似于給常量取別名,而且還會(huì)發(fā)生截?cái)?這個(gè)說截?cái)嘁膊惶线m,這會(huì)產(chǎn)生一個(gè)臨時(shí)變量,我把原理圖放在下面.

int main()
{
	double d = 1.2;
	const int& a = d;
	cout << "&a :" <<&a << endl;
	cout << "&d :" <<&d << endl;
	return 0;
}

引用的場景

我們需要來看看引用的使用場景,它主要有兩大作用.

  • 做參數(shù)
  • 做返回值

做參數(shù)

我們可以使用引用來交換兩個(gè)整型數(shù)據(jù):

#include <stdio.h>
void swap(int& a, int& b)
{
	int c = a;
	a = b;
	b = c;
}
int main()
{
	int a = 10;
	int b = 20;
	printf("交換前  a = %d b = %d\n", a, b);
	swap(a, b);
	printf("交換后  a = %d b = %d\n", a, b);
	return 0;
}

但是使用引用作為參數(shù)可能會(huì)出現(xiàn)權(quán)限不太匹配的錯(cuò)誤,所以說我們需要const修飾.

下面就是由于權(quán)限問題,我們沒有辦法來給常量卻別名,這就需要const修飾.

void func(int& a)
{
}
int main()
{
	int a = 10;
	double b = 10.2;

	func(a);
	func(10);
	func(b);
	return 0;
}

返回值

我們先看看返回值的原理,再說說引用做返回值.

返回值的原理:

我們需要談?wù)劸幾g器是如何使用返回值的,以下面的代碼為例.

int func()
{
	int n = 1;
	n++;
	return n;
}

int main()
{
	int ret = func();
	return 0;
}

編譯器會(huì)看這個(gè)返回值的空間大不大,如果不大,就把的數(shù)據(jù)放到一個(gè)寄存器中,如果很大,看編譯器的機(jī)制,有的編譯器甚至可能在main函數(shù)中開辟這個(gè)空間來臨時(shí)保存數(shù)據(jù).

這是由于當(dāng)函數(shù)結(jié)束后,函數(shù)棧幀會(huì)銷毀,n的空間也會(huì)被釋放,所以要有一個(gè)寄存器來保存數(shù)據(jù).

下面的代碼就可以表現(xiàn)出來:

int main()
{
	int& ret = func();
	return 0;
}

引用做返回值

引用做返回值會(huì)有很大的要求,這個(gè)和我們普通的返回值可不一樣.說實(shí)話,我不想和大家分享的那么深,但是已經(jīng)到這里了,只能這樣了.

下面的代碼,我們可以理解,就是給靜態(tài)變量n取一個(gè)別名,我們把它返回到了ret.

int& func()
{
	static int n = 1;
	n++;
	return n;
}
int main()
{
	int ret = func();
	cout << ret << endl;
	return 0;
}

也就是說我們n取一個(gè)別名,把這個(gè)別名作為返回值返回給函數(shù)調(diào)用者.

int& func()
{
	static int n = 1;
	n++;
	cout << &n << endl;
	return n;
}

int main()
{
	int& ret = func();     //  注意 是 int&
	cout << &ret << endl;
	return 0;
}

注意事項(xiàng):

到這里我們就可以看看引用做返回值的注意事項(xiàng)了,我們一定要保證做返回值得引用得數(shù)據(jù)在函數(shù)棧幀銷毀后空間不被釋放,否則就會(huì)發(fā)生下面得事情.

我們得到的是一個(gè)隨機(jī)值,我們拿到了變量 n 的別名,但是在func結(jié)束后空間就被釋放了,下一次函數(shù)的調(diào)用函數(shù)棧幀會(huì)覆這篇空間,運(yùn)氣好的話,我們有可能拿到準(zhǔn)確值,但是無論如何訪問都越界了.

int& func()
{
	int n = 1;
	n++;
	return n;
}

int main()
{
	int& ret = func();
	printf("這是一條華麗的分割線\n");
	cout << ret << endl;
	cout << ret << endl;
	return 0;
}

引用不會(huì)開辟空間

前面我們說了傳參需要有一定的要求,但是這不是說引用做參數(shù)不行,我們使用引用傳參不會(huì)發(fā)生拷貝,這極大的提高了代碼的效率.

我們定義一個(gè)大點(diǎn)的結(jié)構(gòu)體,來看看拷貝傳參和引用傳參的效率.一般情況下相差大概20倍左右.

typedef struct A 
{
	int arr[10000];
} A;

void func1(A a)
{

}

void func2(A& a)
{

}

void TestRefAndValue()
{
	A a;

	// 以值作為函數(shù)參數(shù)
	size_t begin1 = clock();
	for (size_t i = 0; i < 100000; ++i)
		func1(a);
	size_t end1 = clock();

	// 以引用作為函數(shù)參數(shù)
	size_t begin2 = clock();
	for (size_t i = 0; i < 100000; ++i)
		func2(a);
	size_t end2 = clock();

	// 分別計(jì)算兩個(gè)函數(shù)運(yùn)行結(jié)束后的時(shí)間
	cout << "func1(A)-time:" << end1 - begin1 << endl;
	cout << "func2(A&)-time:" << end2 - begin2 << endl;
}

int main()
{
	TestRefAndValue();
	return 0;
}

引用和指針比較

它們有一個(gè)本質(zhì)的區(qū)別,在語法概念上引用就是一個(gè)別名,沒有獨(dú)立空間,和其引用實(shí)體共用同一塊空間 ,但是引用的底層還是開辟空間的,因?yàn)?strong>引用是按照指針方式來實(shí)現(xiàn).我們看看匯編代碼.

  • 引用不會(huì)開辟空間,但是指針會(huì)開辟相應(yīng)的空間.

底層引用和指針是一樣的.

int main()
{
	int a = 10;
	//語法沒有開辟空間  底層開辟了
	int& b = a;
	b = 20;

	//語法開辟空間  底層也開辟了
	int* pa = &a;
	*pa = 20;
	return 0;

我們來看看它們其他的比較小的區(qū)別,我就不詳細(xì)舉例了.

  • 引用在定義時(shí)必須初始化,指針沒有要求
  • 引用在初始化時(shí)引用一個(gè)實(shí)體后,就不能再引用其他實(shí)體,而指針可以在任何時(shí)候指向任何一個(gè)同類型實(shí)體
  • 沒有NULL引用,但有NULL指針
  • sizeof中含義不同:引用結(jié)果為引用類型的大小,但指針始終是地址空間所占字節(jié)個(gè)數(shù)(32位平臺(tái)下占4個(gè)字節(jié))
  • 引用自加即引用的實(shí)體增加1,指針自加即指針向后偏移一個(gè)類型的大小
  • 有多級(jí)指針,但是沒有多級(jí)引用
  • 訪問實(shí)體方式不同,指針需要顯式解引用,引用編譯器自己處理
  • 引用比指針使用起來相對更安全

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

以inline修飾的函數(shù)叫做內(nèi)聯(lián)函數(shù),編譯時(shí)C++編譯器會(huì)在調(diào)用內(nèi)聯(lián)函數(shù)的地方展開,沒有函數(shù)壓棧的開銷,內(nèi)聯(lián)函數(shù)提升程序運(yùn)行的效率,本質(zhì)來說就和我們的宏比較相似.

記住我們不關(guān)心他們在那個(gè)過程中展開,只需要記住他會(huì)展開就可以了.我么看看內(nèi)聯(lián)函數(shù)的知識(shí)點(diǎn).

為何存在 內(nèi)聯(lián)函數(shù)

對于一些比較小的函數(shù),我們總是調(diào)用函數(shù)開銷比較的,我們是不是存在一個(gè)方法減少這個(gè)開銷呢?C語言通過宏來實(shí)現(xiàn),C++支持C語言,所以我們可以通過行來實(shí)現(xiàn),那么你給我寫一個(gè)兩數(shù)現(xiàn)相加的宏,要是你寫的和下面的不一樣,就代表你忘記了一部分知識(shí)點(diǎn).

#define ADD(x,y) ((x) + (y))       //不帶  ;   括號(hào)要帶

我們就可以理解了,宏很好,但是寫出一個(gè)正確的宏很困難,但是寫一個(gè)函數(shù)就不一樣了,所以一些大佬就發(fā)明了內(nèi)斂函數(shù),用來替代宏的部分功能.

展開短小的函數(shù)

函數(shù)內(nèi)聯(lián)不內(nèi)聯(lián)不是你說了算,我們用inline修飾就是告訴編譯器這個(gè)函數(shù)可以展開,至于是否展開還是看編譯器,一般之后展開比較短小的函數(shù),較大的函數(shù)不會(huì)展開,像遞歸的那些也不可以.

inline void swap(int& x, int& y)
{
	int ret = x;
	x = y;
	y = ret;
}

int main()
{
	int a = 1;
	int b = 2;
	swap(a,b);
	cout << "a: " << a << endl;
	cout << "b: " << b << endl;
	return 0;
}

我們來看看內(nèi)聯(lián)函數(shù),如果函數(shù)不是內(nèi)聯(lián)了的,匯編語言會(huì)call

void swap(int& x, int& y)
{
	int ret = x;
	x = y;
	y = ret;
}

如果在上述函數(shù)前增加inline關(guān)鍵字將其改成內(nèi)聯(lián)函數(shù),在編譯期間編譯器會(huì)用函數(shù)體替換函數(shù)的調(diào)用.

由于我們使用的是VS編譯器,這里需要看看內(nèi)聯(lián)函數(shù)的匯編語言.

在release模式下,查看編譯器生成的匯編代碼中是否存在call swap,但是編譯器會(huì)發(fā)生優(yōu)化,我們通過debug模式下,但是需要設(shè)置.

inline修飾的較短函數(shù)展開了,沒有call

inline void swap(int& x, int& y)
{
	int ret = x;
	x = y;
	y = ret;
}

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

我們需要來看看函數(shù)的基本的特性

  • inline是一種以空間換時(shí)間的做法,省去調(diào)用函數(shù)額開銷。所以代碼很長或者有循環(huán) / 遞歸的函數(shù)不適宜使用作為內(nèi)聯(lián)函數(shù)。
  • inline 對于編譯器而言只是一個(gè)建議,編譯器會(huì)自動(dòng)優(yōu)化,如果定義為inline的函數(shù)體內(nèi)有循環(huán)/遞歸等,編譯器優(yōu)化時(shí)會(huì)忽略內(nèi)聯(lián)。
  • inline不建議聲明和定義分離,分離會(huì)導(dǎo)致鏈接錯(cuò)誤。因?yàn)閕nline被展開,就沒有函數(shù)地址了,鏈接就會(huì)找不到。

較大的函數(shù)編譯器不會(huì)發(fā)生內(nèi)聯(lián)

編譯器會(huì)自動(dòng)判別這個(gè)函數(shù)給不該內(nèi)聯(lián),要是一個(gè)函數(shù)比較大,里面存在遞歸,那么還不如不展開呢.一般是10行為依據(jù).

inline void f()
{
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
}
int main()
{
	f();
	return 0;
}

聲明定義一起

如果我們聲明和定義的分離,運(yùn)行時(shí)編譯器會(huì)找不到這個(gè)函數(shù)的地址的.

這里我么建議直接把內(nèi)聯(lián)函數(shù)直接放到自定義的頭文件中

inline void f()
{
}

到此這篇關(guān)于C++ 引用與內(nèi)聯(lián)函數(shù)詳情的文章就介紹到這了,更多相關(guān)C++ 引用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C或C++報(bào)錯(cuò):ld returned 1 exit status報(bào)錯(cuò)的原因及解決方法

    C或C++報(bào)錯(cuò):ld returned 1 exit status報(bào)錯(cuò)的原因及解

    這篇文章主要介紹了C或C++報(bào)錯(cuò):ld returned 1 exit status報(bào)錯(cuò)的原因及解決方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-02-02
  • 詳解C++中存儲(chǔ)類的使用

    詳解C++中存儲(chǔ)類的使用

    存儲(chǔ)類定義?C++?程序中變量/函數(shù)的范圍(可見性)和生命周期。這些說明符放置在它們所修飾的類型之前。auto、register、static、extern和mutable是C++程序中常用的存儲(chǔ)類,本文主要介紹了它們的使用方法,需要的可以參考一下
    2022-12-12
  • 詳解C++ 創(chuàng)建文件夾的四種方式

    詳解C++ 創(chuàng)建文件夾的四種方式

    這篇文章主要介紹了C++ 創(chuàng)建文件夾的四種方式,本文示例都是在 E:\database 路徑下創(chuàng)建一個(gè)叫做 testFolder 的文件夾,文章通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2021-11-11
  • C/C++高精度運(yùn)算(大整數(shù)運(yùn)算)詳細(xì)講解

    C/C++高精度運(yùn)算(大整數(shù)運(yùn)算)詳細(xì)講解

    高精度算法的本質(zhì)是把大數(shù)拆成若干固定長度的塊,然后對每一塊進(jìn)行相應(yīng)的運(yùn)算,下面這篇文章主要給大家介紹了關(guān)于C/C++高精度運(yùn)算(大整數(shù)運(yùn)算)的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • c/c++ 利用sscanf進(jìn)行數(shù)據(jù)拆分操作

    c/c++ 利用sscanf進(jìn)行數(shù)據(jù)拆分操作

    這篇文章主要介紹了c/c++ 利用sscanf進(jìn)行數(shù)據(jù)拆分操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 解決VC++編譯報(bào)錯(cuò)error C2248的方案

    解決VC++編譯報(bào)錯(cuò)error C2248的方案

    這篇文章主要介紹了解決VC++編譯報(bào)錯(cuò)error C2248的方案的相關(guān)資料,需要的朋友可以參考下
    2015-11-11
  • C++面試八股文之std::string實(shí)現(xiàn)方法

    C++面試八股文之std::string實(shí)現(xiàn)方法

    這篇文章主要介紹了C++面試八股文:std::string是如何實(shí)現(xiàn)的,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-06-06
  • 帶你了解C++的動(dòng)態(tài)內(nèi)存分配

    帶你了解C++的動(dòng)態(tài)內(nèi)存分配

    今天小編就為大家分享一篇關(guān)于關(guān)于C++動(dòng)態(tài)分配內(nèi)存的介紹,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2021-08-08
  • C++實(shí)現(xiàn)list增刪查改模擬的示例代碼

    C++實(shí)現(xiàn)list增刪查改模擬的示例代碼

    本文主要介紹了C++實(shí)現(xiàn)list增刪查改模擬,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-12-12
  • C語言細(xì)致講解線程同步的集中方式

    C語言細(xì)致講解線程同步的集中方式

    多線程中的線程同步可以使用,CreateThread,CreateMutex 互斥鎖實(shí)現(xiàn)線程同步,通過臨界區(qū)實(shí)現(xiàn)線程同步,Semaphore 基于信號(hào)實(shí)現(xiàn)線程同步,CreateEvent 事件對象的同步,以及線程函數(shù)傳遞單一參數(shù)與多個(gè)參數(shù)的實(shí)現(xiàn)方式
    2022-05-05

最新評(píng)論