最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++?函數(shù)重載背后的原理

 更新時(shí)間:2022年05月07日 10:25:29   作者:玄鳥(niǎo)軒墨  
這篇文章主要介紹了C++?函數(shù)重載背后的原理,我們不僅僅需要學(xué)會(huì)重載的使用,更要了解C++為什么支持函數(shù)重載,下面我們一起進(jìn)入文章學(xué)習(xí)該內(nèi)容吧

一、函數(shù)重載

我們可能對(duì)函數(shù)很是熟悉,但是重載又是什么意思呢?我們先來(lái)用一個(gè)具體的場(chǎng)景來(lái)分享.

一天,張三的老板要你寫(xiě)一個(gè)兩位數(shù)相加的函數(shù),張三心想這不很簡(jiǎn)單嗎?手指一動(dòng),結(jié)果就出來(lái)了,挺簡(jiǎn)單的嘛.

int add(int x, int y)
{
	return x + y;
}

現(xiàn)在老板看張三的代碼立馬火了,你是怎么想的,要是我想12,10.9相加呢?你這個(gè)就只能兩個(gè)整型相加,回去修改!!!張三聽(tīng)到老板的話不由得反駁道:這怎么改,總不能再寫(xiě)一個(gè)add1,add2…吧.老板聽(tīng)到了張三的嘟囔,生氣道,你沒(méi)有學(xué)過(guò)函數(shù)重載嗎?看看下面的代碼,回去好好學(xué)習(xí)學(xué)習(xí),基礎(chǔ)都不扎實(shí).

張三看到代碼,不由大吃一驚,C++還可以這么寫(xiě)?好神奇啊,我要好好看看書(shū).

int add(int x, int y)
{
	return x + y;
}
double add(double x, int y)
{
	return x + y;
}
double add(int x, double y)
{
	return x + y;
}

我們可不希望張三這種事發(fā)生在我們身上,先來(lái)看看函數(shù)重載的定義

函數(shù)重載:是函數(shù)的一種特殊情況,C++允許在同一作用域中聲明幾個(gè)功能類(lèi)似的同名函數(shù),這些同名函數(shù)的
形參列表(參數(shù)個(gè)數(shù) 或 類(lèi)型 或 順序)必須不同,常用來(lái)處理實(shí)現(xiàn)功能類(lèi)似數(shù)據(jù)類(lèi)型不同的問(wèn)題 .

可能大家不喜歡看定義,我這里給一個(gè)總結(jié).

函數(shù)重載要滿足下面的要求.

  • 函數(shù)名相同
  • 參數(shù)的類(lèi)型,個(gè)數(shù),順序有一個(gè)不同就可以了
  • 返回類(lèi)型不做要求

二、函數(shù)重載的原理

一般情況下,我們知道了函數(shù)重載到會(huì)應(yīng)用就可以了,但是對(duì)于我們來(lái)說(shuō)需要我們看看他們的原理,為什么C語(yǔ)言不支持重載,C++支持重載?這些都是問(wèn)題.

三、為何C++可以支持重載

我們先用C++的編譯器簡(jiǎn)單的看看如何執(zhí)行程序,下面是我在Linux環(huán)境下使用g++來(lái)完成的,大家要是不太懂,可以先不管,直接理解C++的原理.

我們先來(lái)看看現(xiàn)象,發(fā)現(xiàn)C++可以精準(zhǔn)的找到需要匹配的函數(shù),這是我們所疑惑的.

// test.h
#pragma once 
#include <iostream>
#include <stdio.h>
using std::cout;
void func(int a, double b);
void func(double a, int b);
//test.cpp
#include "test.h"
//寫(xiě)兩個(gè)函數(shù)   函數(shù)形成重載
void func(int a, double b)
{
	printf("%d %lf", a, b);
}
void func(double a, int b)
{
	printf("%lf %d", a, b);
}
//Mian.cpp

#include "test.h"
int main()
{
	func(10, 2.20);
	return 0;
}

1.程序的編譯鏈接

關(guān)于這一點(diǎn),我們先簡(jiǎn)單的說(shuō)說(shuō),之前我們?cè)敿?xì)的談過(guò).一個(gè)文件變成一個(gè)可執(zhí)行程序需要經(jīng)過(guò)下面4個(gè)步驟.

  • 預(yù)處理 宏替換 頭文件展開(kāi) 注釋替換 main.cpp -> main.i test.cpp -> test.i
  • 編譯 檢查語(yǔ)法 ,代碼變換成匯編語(yǔ)言 main.i -> main.s test.i -> test.s
  • 匯編 匯編語(yǔ)言變成二進(jìn)制語(yǔ)言,各個(gè)文件變成目標(biāo)文件 main.s -> main.o test.s -> test.o
  • 鏈接 多個(gè)目標(biāo)文件+鏈接庫(kù)發(fā)生鏈接

這里我們需要重點(diǎn)談?wù)勬溄?這是我們今天最重要的一部分

鏈接就僅僅只是目標(biāo)文件的合并嗎?不是的,它要完成的任務(wù)很多,其中最重要的就是<font color = red>找到函數(shù)的地址,鏈接對(duì)應(yīng)上,合并到一起</font>

當(dāng)我們進(jìn)行過(guò)頭文件的展開(kāi)后,Main.cpp中有func函數(shù)的聲明和調(diào)用.在編譯和匯編的過(guò)程中存在一個(gè)符號(hào)表,這個(gè)符號(hào)表記錄了函數(shù)的定義以及相應(yīng)的映射.這是很重要的.符號(hào)表里面包含了函數(shù)名和函數(shù)的地址.

每一個(gè)目標(biāo)文件(.o)都包含一個(gè)符號(hào)表和一系列指令,我們看看入和完成函數(shù)鏈接.

現(xiàn)在到mian.o的指令這里了,前面的一些列指令都正常經(jīng)行,直到它遇到了func這個(gè)點(diǎn),要是看過(guò)C語(yǔ)言的匯編語(yǔ)言的朋友們可能對(duì)下面的比較熟悉.

到了func這里,編譯器開(kāi)始call (func: ?),編譯器不知道func的地址,但是前面頭文件的的展開(kāi)中func函數(shù)已經(jīng)聲明了,所以編譯器知道了func是一個(gè)函數(shù).就先給它一個(gè)無(wú)效的地址.當(dāng)程序進(jìn)行鏈接時(shí),編譯器一看它是一個(gè)無(wú)效地址,會(huì)拿函數(shù)名和其他的.o文件里面的符號(hào)表去碰,碰到了就填上,找不到就會(huì)報(bào)連接錯(cuò)誤.

四、C語(yǔ)言為何不支持重載

到這里就可以明白了,當(dāng)我們拿函數(shù)名去碰的時(shí)候,符號(hào)表里面存在多個(gè)相同的函數(shù)名,編譯器就不會(huì)識(shí)別該用哪個(gè).更何況存在相同函數(shù)名的.c文件有時(shí)都不可能編譯過(guò).

gcc對(duì)函數(shù)名都不會(huì)做任何處理,這也是C語(yǔ)言不支持函數(shù)重載的原因.

1.C++為何可以支持函數(shù)重載

到這里我們就可以得到了結(jié)果,既然在鏈接的時(shí)候無(wú)效的函數(shù)會(huì)拿函數(shù)名去其他的符號(hào)表里面去碰,那么只要我們看看重載的函數(shù)名像不像同就可以了,大家可能會(huì)有些疑惑,重載的函數(shù)名不是相同的嗎?是的,但是C++編譯器會(huì)做一定的處理.這里每個(gè)編譯器都有自己的函數(shù)名修飾規(guī)則 這就是C++ 支持重載的原理.

這就是C可以支持重載的原因,g的函數(shù)修飾后變成【_Z+函數(shù)名長(zhǎng)度+函數(shù)名+類(lèi)型首字母1+類(lèi)型首字母2…】,也是我們只對(duì)參數(shù)列表做了要求,對(duì)返回值不做要求的原因.

五、C++和C語(yǔ)言相互調(diào)用

我們都知道C++支持C語(yǔ)言的大部分語(yǔ)法,C++和C語(yǔ)言可以相互調(diào)用嗎?實(shí)際上是可以的,在一個(gè)大型程序中,有的部門(mén)可能使用的是C寫(xiě)的的函數(shù),有的部門(mén)可能用的C++,要是他們不能相互使用那就打臉了.

1.創(chuàng)建靜態(tài)庫(kù)

我們可以把自己寫(xiě)的代碼編譯成一個(gè)靜態(tài)庫(kù)或者動(dòng)態(tài)庫(kù),這里我以靜態(tài)庫(kù)舉例,看看如何在VS中中創(chuàng)建一個(gè)靜態(tài)庫(kù).

2.C++調(diào)用C

我們已經(jīng)有了一個(gè)C語(yǔ)言的靜態(tài)庫(kù),現(xiàn)在有一個(gè)C++的項(xiàng)目需要使用這個(gè)靜態(tài)庫(kù),我們?cè)撊绾问褂媚?需要分為下面幾個(gè)步驟

下面這兩張圖片都是修改環(huán)境的設(shè)置,我使用的是VS2013,其他的大概應(yīng)該差不多,大家依次來(lái)修改就可以了.

到這里我們就可以調(diào)用C語(yǔ)言的靜態(tài)庫(kù)了,讓我們來(lái)看看結(jié)果吧.

#include "../../Heap/Heap/heap.h"  //相對(duì)路徑
int main()
{
	MyHeap myHeap;
	InitMyHeap(&myHeap);
	HeapPush(&myHeap, 1);
	HeapPush(&myHeap, 2);
	HeapPush(&myHeap, 3);
	Display(&myHeap);
	return 0;
}

這為什么報(bào)錯(cuò)?我們不是已經(jīng)設(shè)置好了靜態(tài)庫(kù)了嗎?實(shí)際上這種錯(cuò)誤是很容易分析出來(lái)的,當(dāng)C++去調(diào)用C語(yǔ)言的函數(shù)時(shí),C++會(huì)自動(dòng)修改函數(shù)名,當(dāng)時(shí)C語(yǔ)言不會(huì)啊,所以他們就不會(huì)碰到一起,鏈接就會(huì)出錯(cuò).

extern “C”

既然編譯器不能自動(dòng)識(shí)別C語(yǔ)言的函數(shù)名,我們告訴編譯器一下不就可以了嗎.extern “C” 就是這種作用.

有時(shí)候在C++工程中可能需要將某些函數(shù)按照 C 的風(fēng)格來(lái)編譯在函數(shù)前加 extern “C” ,意思是告訴編譯器,
將該函數(shù)按照
 C 語(yǔ)言規(guī)則來(lái)編譯。比如:tcmalloc是google用C++實(shí)現(xiàn)的一個(gè)項(xiàng)目,他提供tcmallc()和tcfree
兩個(gè)接口來(lái)使用,但如果是C項(xiàng)目就沒(méi)辦法使用,那么他就使用extern “C”來(lái)解決

extern "C"   // 告知這是C語(yǔ)言的函數(shù)聲明
{
	#include "../../Heap/Heap/heap.h"
}
int main()
{
	MyHeap myHeap;
	InitMyHeap(&myHeap);
	HeapPush(&myHeap, 1);
	HeapPush(&myHeap, 2);
	HeapPush(&myHeap, 3);
	Display(&myHeap);
	return 0;
}

extern “C” 原理

我們需要來(lái)看看extern “C” 的原理,使用了extern “C” 后,在C++在進(jìn)行編譯的時(shí)候函數(shù)名字就依據(jù)C語(yǔ)言的方法來(lái)修改了,不在變成C++ 的規(guī)則.extern "C"可以單獨(dú)修飾函數(shù),也可以修飾一系列函數(shù),使用代碼塊.

// test.h
#pragma once 
#include <iostream>
#include <stdio.h>
extern "C" void func(int a, double b);
//test.cpp

#include "test.h"
//寫(xiě)兩個(gè)函數(shù)   函數(shù)形成重載
void func(int a, double b)
{
	printf("%d %lf", a, b);
}
//Mian.cpp

#include "test.h"
int main()
{
	func(10, 2.20);
	return 0;
}

3.C語(yǔ)言調(diào)用C++

那么C語(yǔ)言可以調(diào)用C++ 的嗎?可以了,不過(guò)也需要一些段來(lái)完成.如何讓C語(yǔ)言去識(shí)別C++的規(guī)則呢?這是我們需要考慮的.

我們已經(jīng)把庫(kù)改成的了C++的靜態(tài)庫(kù)了.

#include "../../Heap/Heap/heap.h"
int main()
{
	MyHeap myHeap;
	InitMyHeap(&myHeap);
	HeapPush(&myHeap, 1);
	HeapPush(&myHeap, 2);
	HeapPush(&myHeap, 3);
	Display(&myHeap);
	return 0;
}

我們無(wú)法讓C語(yǔ)言的編譯器去識(shí)別C++ 的函數(shù)的命名,那么我們是不是可以在函數(shù)一編譯的時(shí)候就完成函數(shù)名依照C語(yǔ)言來(lái)說(shuō).這就很簡(jiǎn)單了.

但是即使是這樣,C語(yǔ)言仍舊會(huì)報(bào)錯(cuò),原因在于在頭文件展開(kāi)的時(shí)候,C語(yǔ)言根本不識(shí)別extern “C”,所以我們就需要條件編譯了.

使用條件編譯來(lái)修改的靜態(tài)庫(kù)的方法如下,需要再次編譯.

//方法一
#ifdef __cplusplus    // C++獨(dú)有的
	#define EXTERNC extern "C"
#else 
	#define EXTERNC
#endif


EXTERNC extern void InitMyHeap(MyHeap * pHeap);

EXTERNC extern void HeapPush(MyHeap* pHeap, HPDataType x);
EXTERNC extern bool IsFull(MyHeap* pHeap);
EXTERNC extern bool IsEmpty(MyHeap* pHeap);
EXTERNC extern int HeapSize(MyHeap* pHeap);
EXTERNC extern void adjustDown(MyHeap* pHeap);
EXTERNC extern void adjustUp(MyHeap* pHeap);
EXTERNC extern void Display(MyHeap* pHeap);
EXTERNC extern HPDataType HeapTop(MyHeap* pHeap);
EXTERNC extern void HeapPop(MyHeap* pHeap);
//方法 二
#ifdef __cplusplus
extern "C"
{
#endif

	extern void InitMyHeap(MyHeap * pHeap);

	extern void HeapPush(MyHeap* pHeap, HPDataType x);
	extern bool IsFull(MyHeap* pHeap);
	extern bool IsEmpty(MyHeap* pHeap);
	extern int HeapSize(MyHeap* pHeap);
	extern void adjustDown(MyHeap* pHeap);
	extern void adjustUp(MyHeap* pHeap);
	extern void Display(MyHeap* pHeap);
	extern HPDataType HeapTop(MyHeap* pHeap);
	extern void HeapPop(MyHeap* pHeap);
#ifdef __cplusplus
}
#endif

這樣就解決了.

注意,這里有一點(diǎn)需要注意的,當(dāng)我們C語(yǔ)言調(diào)用C++靜態(tài)庫(kù)的時(shí)候,最起碼我們實(shí)際需要的的那部分代碼在extern "C"修飾的函數(shù)中不能發(fā)生重載.

六、C++ 注意事項(xiàng)

這個(gè)注意事項(xiàng)主要是依據(jù)extern "C"來(lái)談的,有些比較偏僻的內(nèi)容需要關(guān)注下.

1.extern "C"修飾的函數(shù)和一個(gè)函數(shù)完全一樣

在extern "C"修飾的函數(shù)模塊外面存在了一個(gè)完全一摸一樣的的函數(shù),這個(gè)編譯器不會(huì)給通過(guò)的.

#ifdef __cplusplus
extern "C"
{
#endif 
	void func(int a, int b)
	{
		printf("C : %d %d\n", a, b);
	}
#ifdef __cplusplus
}
#endif 
//完全一樣
void func(int a, int b)
{
	printf("C : %d %d\n", a, b);
}

2.extern "C"修飾的函數(shù)和一個(gè)函數(shù)構(gòu)成重載

在extern "C"修飾的函數(shù)模塊外面一個(gè)函數(shù)構(gòu)成重載這種編譯器可以通過(guò)的,但是extern "C"修飾的命名方法仍舊還是按照C語(yǔ)言的方式,構(gòu)成重載的是C++的方式.

#include <iostream>
using namespace std;

#ifdef __cplusplus
extern "C"
{
#endif 
	void func(int a, int b)
	{
		printf("C : %d %d\n", a, b);
	}

#ifdef __cplusplus
}
#endif 

void func(double a, int b)
{
	printf("C++: %lf %d\n", a, b);
}

int main()
{
	func(1, 2);
	func(1.11, 2);
	return 0;
}

到此這篇關(guān)于C++ 函數(shù)重載背后的原理的文章就介紹到這了,更多相關(guān)C++ 函數(shù)重載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++全面覆蓋內(nèi)存管理知識(shí)講解

    C++全面覆蓋內(nèi)存管理知識(shí)講解

    本章主要介紹C語(yǔ)言與C++的內(nèi)存管理,以C++的內(nèi)存分布作為引入,介紹C++不同于C語(yǔ)言的內(nèi)存管理方式(new delete對(duì)比 malloc free),感興趣的朋友來(lái)看看吧
    2022-06-06
  • 最新評(píng)論

    澄迈县| 建昌县| 棋牌| 尖扎县| 南岸区| 寿宁县| 呼伦贝尔市| 南乐县| 尉犁县| 象州县| 南宫市| 逊克县| 平南县| 香格里拉县| 青川县| 仪陇县| 雅江县| 峨边| 鸡东县| 许昌市| 图木舒克市| 广汉市| 子洲县| 宜宾市| 昌都县| 昌吉市| 漳浦县| 四子王旗| 太康县| 南投市| 博白县| 五原县| 岐山县| 巫山县| 广东省| 怀宁县| 新闻| 郴州市| 阿尔山市| 滕州市| 北川|