亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C++?函數重載背后的原理

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

一、函數重載

我們可能對函數很是熟悉,但是重載又是什么意思呢?我們先來用一個具體的場景來分享.

一天,張三的老板要你寫一個兩位數相加的函數,張三心想這不很簡單嗎?手指一動,結果就出來了,挺簡單的嘛.

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

現在老板看張三的代碼立馬火了,你是怎么想的,要是我想12,10.9相加呢?你這個就只能兩個整型相加,回去修改!!!張三聽到老板的話不由得反駁道:這怎么改,總不能再寫一個add1,add2…吧.老板聽到了張三的嘟囔,生氣道,你沒有學過函數重載嗎?看看下面的代碼,回去好好學習學習,基礎都不扎實.

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

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ā)生在我們身上,先來看看函數重載的定義

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

可能大家不喜歡看定義,我這里給一個總結.

函數重載要滿足下面的要求.

  • 函數名相同
  • 參數的類型,個數,順序有一個不同就可以了
  • 返回類型不做要求

二、函數重載的原理

一般情況下,我們知道了函數重載到會應用就可以了,但是對于我們來說需要我們看看他們的原理,為什么C語言不支持重載,C++支持重載?這些都是問題.

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

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

我們先來看看現象,發(fā)現C++可以精準的找到需要匹配的函數,這是我們所疑惑的.

// 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"
//寫兩個函數   函數形成重載
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.程序的編譯鏈接

關于這一點,我們先簡單的說說,之前我們詳細的談過.一個文件變成一個可執(zhí)行程序需要經過下面4個步驟.

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

這里我們需要重點談談鏈接,這是我們今天最重要的一部分

鏈接就僅僅只是目標文件的合并嗎?不是的,它要完成的任務很多,其中最重要的就是<font color = red>找到函數的地址,鏈接對應上,合并到一起</font>

當我們進行過頭文件的展開后,Main.cpp中有func函數的聲明和調用.在編譯和匯編的過程中存在一個符號表,這個符號表記錄了函數的定義以及相應的映射.這是很重要的.符號表里面包含了函數名和函數的地址.

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

現在到mian.o的指令這里了,前面的一些列指令都正常經行,直到它遇到了func這個點,要是看過C語言的匯編語言的朋友們可能對下面的比較熟悉.

到了func這里,編譯器開始call (func: ?),編譯器不知道func的地址,但是前面頭文件的的展開中func函數已經聲明了,所以編譯器知道了func是一個函數.就先給它一個無效的地址.當程序進行鏈接時,編譯器一看它是一個無效地址,會拿函數名和其他的.o文件里面的符號表去碰,碰到了就填上,找不到就會報連接錯誤.

四、C語言為何不支持重載

到這里就可以明白了,當我們拿函數名去碰的時候,符號表里面存在多個相同的函數名,編譯器就不會識別該用哪個.更何況存在相同函數名的.c文件有時都不可能編譯過.

gcc對函數名都不會做任何處理,這也是C語言不支持函數重載的原因.

1.C++為何可以支持函數重載

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

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

五、C++和C語言相互調用

我們都知道C++支持C語言的大部分語法,C++和C語言可以相互調用嗎?實際上是可以的,在一個大型程序中,有的部門可能使用的是C寫的的函數,有的部門可能用的C++,要是他們不能相互使用那就打臉了.

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

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

2.C++調用C

我們已經有了一個C語言的靜態(tài)庫,現在有一個C++的項目需要使用這個靜態(tài)庫,我們該如何使用呢?需要分為下面幾個步驟

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

到這里我們就可以調用C語言的靜態(tài)庫了,讓我們來看看結果吧.

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

這為什么報錯?我們不是已經設置好了靜態(tài)庫了嗎?實際上這種錯誤是很容易分析出來的,當C++去調用C語言的函數時,C++會自動修改函數名,當時C語言不會啊,所以他們就不會碰到一起,鏈接就會出錯.

extern “C”

既然編譯器不能自動識別C語言的函數名,我們告訴編譯器一下不就可以了嗎.extern “C” 就是這種作用.

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

extern "C"   // 告知這是C語言的函數聲明
{
	#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” 原理

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

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

#include "test.h"
//寫兩個函數   函數形成重載
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語言調用C++

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

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

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

我們無法讓C語言的編譯器去識別C++ 的函數的命名,那么我們是不是可以在函數一編譯的時候就完成函數名依照C語言來說.這就很簡單了.

但是即使是這樣,C語言仍舊會報錯,原因在于在頭文件展開的時候,C語言根本不識別extern “C”,所以我們就需要條件編譯了.

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

//方法一
#ifdef __cplusplus    // C++獨有的
	#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

這樣就解決了.

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

六、C++ 注意事項

這個注意事項主要是依據extern "C"來談的,有些比較偏僻的內容需要關注下.

1.extern "C"修飾的函數和一個函數完全一樣

在extern "C"修飾的函數模塊外面存在了一個完全一摸一樣的的函數,這個編譯器不會給通過的.

#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"修飾的函數和一個函數構成重載

在extern "C"修飾的函數模塊外面一個函數構成重載這種編譯器可以通過的,但是extern "C"修飾的命名方法仍舊還是按照C語言的方式,構成重載的是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;
}

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

相關文章

  • Microsoft Visual C++ 程序的部署方法

    Microsoft Visual C++ 程序的部署方法

    由Microsoft Visual C++編譯的程序動態(tài)鏈接到C運行時(/MD 或 /MDd),它必須捆綁C運行DLL的一份拷貝(通常被叫作MSVCRT.DLL 或 MSVCRxx.DLL,其中xx代表Visual C++的版本)
    2013-04-04
  • C語言繪制三角函數曲線

    C語言繪制三角函數曲線

    這篇文章主要為大家詳細介紹了C語言繪制三角函數曲線,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • C/C++使用fmt庫實現格式化字符串

    C/C++使用fmt庫實現格式化字符串

    fmt庫是一個高效、易用的C++格式化庫,可以幫助我們方便地進行字符串格式化、輸出、日志記錄等操作,下面我們就來學習一下fmt格式化字符串的具體操作吧
    2023-12-12
  • C++實現教務管理系統(tǒng)

    C++實現教務管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C++實現教務管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • 緩存處理函數storageKeySuffix操作示例解析

    緩存處理函數storageKeySuffix操作示例解析

    這篇文章主要介紹了淺析緩存處理函數storageKeySuffix操作示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • C語言內嵌匯編API內存搜索引擎實例

    C語言內嵌匯編API內存搜索引擎實例

    這篇文章主要介紹了C語言內嵌匯編API內存搜索引擎實例,涉及匯編語言與內存相關操作,需要的朋友可以參考下
    2014-10-10
  • VC實現的病毒專殺工具完整實例

    VC實現的病毒專殺工具完整實例

    這篇文章主要介紹了VC實現的病毒專殺工具完整實例,詳細講述了針對病毒的進程終止、刪除文件及回復注冊表與啟動項等,同時介紹了與之相關的系統(tǒng)函數,非常具有參考借鑒價值,需要的朋友可以參考下
    2014-10-10
  • C++vector自定義大小方式

    C++vector自定義大小方式

    這篇文章主要介紹了C++vector自定義大小方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • C++中getline()、gets()等函數的用法詳解

    C++中getline()、gets()等函數的用法詳解

    這篇文章主要介紹了C++中getline()、gets()等函數的用法,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-02-02
  • C++變量存儲的生命周期與作用域實例代碼精講

    C++變量存儲的生命周期與作用域實例代碼精講

    這篇文章主要介紹了C++變量存儲的生命周期與作用域,從創(chuàng)建到消亡的完整過程,例如人從出生到死亡的整個過程就是一個生命周期。本文將通過示例為大家詳細講講,感興趣的可以學習一下
    2022-10-10

最新評論