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

C++深入淺出講解函數重載

 更新時間:2022年05月31日 09:57:04   作者:格式化、、  
C++允許多個函數擁有相同的名字,只要它們的參數列表不同就可以,這就是函數的重載(Function?Overloading),借助重載,一個函數名可以有多種用途

前言

自然語言中,一個詞可以有多重含義,人們可以通過上下文來判斷該詞真實的含義,即該詞被重載了。

比如:以前有一個笑話,國有兩個體育項目大家根本不用看,也不用擔心。一個是乒乓球,一個是男足。前者是“誰也贏不了!”,后者是“誰也贏不了!”

函數重載

1.1 函數重載的概念

函數重載:

  1. 它是函數的一種特殊情況,C++允許在同一作用域中同一作用域中聲明幾個功能類似的同名函數
  2. 函數重載的關鍵是函數的參數列表,也稱為“函數特征標”
  3. 這些同名函數的形參列表(參數個數、類型和順序(不同類型的順序))必須不同,常用來處理實現功能類似數據類型不同的問題
  4. 函數重載也是多態(tài)的一種,多態(tài)指的是“有多種形式”
//C語言不支持重載,C++支持重載
int Add(int left, int right)
{
   return left+right;
}
double Add(double left, double right)
{
   return left+right;
}
int Add(int left, double right)
{
   return left+right;
}
int Add(double left, int right)
{
   return left+right;
}
int main()
{
   Add(10, 20);
   Add(10.0, 20.0);
   Add(10, 20.0);
   Add(10.0, 20.0)
   return 0;
}

下面兩個函數屬于函數重載嗎?

short Add(short left, short right)
{
   return left+right;
}
int Add(short left, short right)
{
   return left+right;
}
int main()
{
   Add(10, 20);
   Add(10, 20);
   return 0;
}

代碼解析:

  1. 上述代碼中的兩個函數不屬于函數重載
  2. 因為重載的形參列表(參數個數、類型和順序)必須不同
  3. 函數重載與函數返回值的類型無關,并且在函數調用時,也是無法識別它的

1.2 函數重載的意義

意義:

在C語言中,想要定義多個不同類型交換數據的子函數,需要不同的函數名來命名,比如SweapA、SweapB…等等

void SweapA(int *pa, int *pb)
{
   int temp = *pa;
   *pa = *pb;
   *pb = temp;
}
void SweapB(double *pa, double *pb)
{
   double temp = *pa;
   *pa = *pb;
   *pb = temp;
}
int main()
{
   int a = 10, b = 20;
   double c = 10.0, d = 20.0;
   SweapA(&a, &b);
   SweapB(&c), &d);
   return 0;
}
  1. 但是,在C++中,通過函數重載,只需要命名一次就可以了
  2. 雖然跟C語言一樣要重復定義函數,但是后面會學到函數模板后,可以很好的解決這個重復定義問題
void Sweap(int *pa, int *pb)
{
   int temp = *pa;
   *pa = *pb;
   *pb = temp;
}
void Sweap(double *pa, double *pb)
{
   double temp = *pa;
   *pa = *pb;
   *pb = temp;
}
int main()
{
   int a = 10, b = 20;
   double c = 10.0, d = 20.0;
   Sweap(&a, &b);
   Sweap(&c), &d);
   return 0;
}

1.3 名字修飾(name Mangling)

名字修飾(name Mangling):

  • C++為了跟蹤每一個重載函數,它都會給這些函數指定一個私密身份
  • 使用C++編譯器編寫函數重載程序時,C++編譯器將執(zhí)行一些奇特的操作 — — ---名稱修飾 或 名稱矯正
  • 它根據函數原型中指定的形參對每個函數名進行加密
  • 對參數數目和類型進行編碼,添加的一組符號符合隨函數形參列表而異,修飾時使用的約定(函數名)隨編譯器而異

為什么C++支持重載,而C語言不支持呢?

  • 在C/C++中,一個程序要運行起來,需要經歷以下幾個階段:預處理、編譯、匯編、鏈接
  • 預處理(.i):文件展開、宏替換、條件編譯、去注釋
  • 編譯(.s):檢查語法是否正確,生成匯編代碼
  • 匯編(.o):將匯編代碼轉化成二進制的機器碼
  • 鏈接(a.out):生成符號表,找調用函數的地址,鏈接匹配,合并到一起

  • 實際我們的項目通常是由多個頭文件和多個源文件構成,當前a.cpp中調用了b.cpp中定義的Add函數
  • 在編譯后鏈接前的處理階段,a.o的目標文件中沒有Add的函數地址,因為Add是在b.cpp中定義的,所以Add的地址在b.o中。那么怎么辦呢?
  • 鏈接器看到a.o調用Add,但是沒有Add的地址,就會到b.o的符號表中找Add的地址,然后鏈接到一起
  • 鏈接時,面對Add函數,鏈接器會使用哪個名字去找呢?這里每個編譯器都有自己的函數名修飾規(guī)則

在Linux下使用gcc和g++編譯器演示函數名被修飾后的名字

采用C語言編譯器編譯后結果(反匯編)

結論:在Linux下,采用gcc編譯完成后,函數名字的修飾沒有發(fā)生改變

采用C++編譯器編譯后結果(反匯編)

結論:在Linux下,采用g++編譯完成后,函數名字的修飾發(fā)生改變,編譯器將函數參數類型信息添加到修改后的名字中

總結

gcc的函數修飾后名字不變。而g++的函數修飾后變成(_Z+函數長度+函數名+類型首字母)

C語言沒辦法支持重載,因為同名函數沒辦法區(qū)分。而C++是通過函數修飾規(guī)則來區(qū)分,只要參數不同,修飾出來的名字就不一樣,就支持了重載

Windows下名字修飾規(guī)則

結論:對比Linux會發(fā)現,windows下C++編譯器對函數名字修飾非常奇怪,但道理都是一樣的

擴展學習:C/C++函數調用約定和名字修飾規(guī)則

C++函數重載

C/C++的調用約定

接下來,再演示一個例子

f.h
#include <stdio.h>

void f(int a, double b);
void f(double b, int a);

f.cpp
#include "f.h"

void f(int a, double b);
{
   printf("%d %lf\n", a, b)
}

void f(double b, int a);
{
   printf("%lf %d\n", b, a)
}
Test.cpp
#include "f.h"

int main()
{
   f(1, 2.222);
   f(2.222, 1);
   return 0;
}

編譯后,生成匯編指令;鏈接時,生成符號表

Linux下g++(C++)編譯器的命名:

Linux下gcc(C)編譯器的命名:

1.4 extern "C"

  • 有時候在C++工程中可能需要將某些函數按照C的風格來編譯
  • 但是,大多數情況下是C工程需要將某些函數按照C++的風格來編譯
  • C可以調用CPP的靜態(tài)/動態(tài)庫,而CPP也可以調用C的靜態(tài)/動態(tài)庫
  • extern “C”是告訴編譯器,它所聲明的函數,是C的庫,要用C的鏈接方式去調用靜態(tài)庫或動態(tài)庫

那么CPP是怎么調用C中的靜態(tài)/動態(tài)庫呢?(vs2022演示)

首先,我們用C來生成一個靜態(tài)庫或動態(tài)庫

Test.h
#include <stdio.h>
void PrintArray(int* p, int n); //顯示數組內容
void InsertSort(int* p, int n); //插入排序
Test.C
#include "Test.h"
void InsertSort(int* p, int n)
{
    for (int i = 0; i < n - 1; ++i)
    {
        int end = i;
        int tmp = p[end + 1];
        while (end >= 0)
        {
            if (tmp < p[end])
            {
                p[end + 1] = p[end];
                --end;
            }
            else
            {
                break;
            }
        }
        p[end + 1] = tmp;
    }
}

配置類型改成靜態(tài)庫后,生成解決方案,就得到后綴.lib文件了

在CPP項目中添加新的庫目錄(這個庫是你生成的靜態(tài)庫的路徑)

增加新的依賴項(依賴項為生成靜態(tài)庫的文件名+后綴"Test.lib")

做完這些準備后,我們來進行編譯程序

  • 編譯后我們發(fā)現鏈接階段時出現了錯誤
  • 原因是:C++調用C時,它們之間的函數命名規(guī)則(名稱修飾)不同
  • 我們需要C++中的extern "C"來解決
  • extern “C”是告訴編譯器,它所聲明的函數,是C的庫,要用C的鏈接方式去調用靜態(tài)庫或動態(tài)庫
extern "C"
{
    //"../"是在當前目錄的上一個目錄中找文件
    #include "../../Test/Test/Test.h"
}
#include <iostream>
using namespace std;
void TestInsertSort()
{
	int Array[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
	InsertSort(Array, sizeof(Array) / sizeof(Array[0]));
	for (int i = 0; i < 10; ++i)
		cout << Array[i] << " ";
	cout << " " << endl;
}
int main()
{
	TestInsertSort();
	return 0;
}

如果C想調用CPP的靜態(tài)或動態(tài)庫呢?

  • C調用CPP庫時,也會遇到名稱修飾的問題
  • 這里需要對CPP的名稱修飾規(guī)則改成C的規(guī)則
  • 這里我們需要用到"條件編譯"來解決問題
Test.h
#include <stdio.h>
#ifdef __cplusplus
      #define EXTERN_C extern "C"
#else 
      #define EXTERN_C
#endif
EXTERN_C void PrintArray(int* p, int n);
EXTERN_C void InsertSort(int* p, int n);
Test.cpp
#include "Test.h"
void PrintArray(int* p, int n)
{
    for (int i = 0; i < n; ++i)
    {
        printf("%d ", p[i]);
    }
    printf("\n");
}
void InsertSort(int* p, int n)
{
    for (int i = 0; i < n - 1; ++i)
    {
        int end = i;
        int tmp = p[end + 1];
        while (end >= 0)
        {
            if (tmp < p[end])
            {
                p[end + 1] = p[end];
                --end;
            }
            else
            {
                break;
            }
        }
        p[end + 1] = tmp;
    }
}

感謝大家支持?。。?/p>

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

相關文章

  • C語言進階:指針的進階(4)

    C語言進階:指針的進階(4)

    這篇文章主要介紹了C語言指針詳解及用法示例,介紹了其相關概念,然后分享了幾種用法,具有一定參考價值。需要的朋友可以了解下
    2021-09-09
  • C語言數據的存儲超詳細講解中篇練習

    C語言數據的存儲超詳細講解中篇練習

    使用編程語言進行編程時,需要用到各種變量來存儲各種信息。變量保留的是它所存儲的值的內存位置。這意味著,當您創(chuàng)建一個變量時,就會在內存中保留一些空間。您可能需要存儲各種數據類型的信息,操作系統(tǒng)會根據變量的數據類型,來分配內存和決定在保留內存中存儲什么
    2022-04-04
  • c++代碼調試方式的幾點建議

    c++代碼調試方式的幾點建議

    這篇文章主要介紹了c++代碼調試方式的幾點建議,幫助大家更好的理解和學習c++,感興趣的朋友可以了解下
    2020-08-08
  • 基于C語言實現2048游戲

    基于C語言實現2048游戲

    這篇文章主要為大家詳細介紹了基于C語言實現2048游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • C語言進階可變參數列表

    C語言進階可變參數列表

    這篇文章主要為大家介紹了C語言進階可變參數列表的示例詳解有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2022-02-02
  • C語言實現按行讀寫文件

    C語言實現按行讀寫文件

    這篇文章主要為大家詳細介紹了C語言實現按行讀寫文件,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • 詳解C++虛函數的工作原理

    詳解C++虛函數的工作原理

    這篇文章主要介紹了C++虛函數的工作原理的的相關資料,文中講解非常細致,代碼幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-06-06
  • Qt兩種定時器使用實現方式

    Qt兩種定時器使用實現方式

    這篇文章主要給大家介紹了關于Qt兩種定時器使用實現方式的相關資料,Qt中的定時器類是QTimer,QTimer不是一個可見的界面組件,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-01-01
  • C++多線程傳參的實現方法

    C++多線程傳參的實現方法

    本文主要介紹了C++多線程傳參的實現方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-04-04
  • vs code 配置c/c++環(huán)境的詳細教程(推薦)

    vs code 配置c/c++環(huán)境的詳細教程(推薦)

    這篇文章主要介紹了vs code 配置c/c++環(huán)境的詳細教程(推薦),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11

最新評論