一. 什么是函數重載?
函數重載是指在同一作用域內,可以有一組具有相同函數名,不同參數列表(參數個數、類型、順序)的函數,這組函數被稱為重載函數。重載函數通常用來聲明一組功能相似的函數,這樣做減少了函數名的數量,避免了名字空間的污染,對於程序的可讀性有很大的好處。
二、為什么要用函數重載
在我們之前學習的C中,我們對一個功能函數要實現不同類型的調用時,就必須得取不同的名稱。如果調用的非常的多,就必須得起好多的名字,這樣就大大增加了工作量,所以在C++中,我們就考慮到了函數重載。
三、 C++函數重載如何實現?
在C++的底層,有重命名機制,比如下面這個函數。
實現一個加法函數,可以對int型、double型、long型進行加法運算。在C++中,我們可以這樣做:
#include<iostream>
using namespace std;
int Add(int left, int right)
{
return left + right;
}
double Add(double left, double right)
{
return left + right;
}
long Add(long left, long right)
{
return left + right;
}
int main()
{
Add(10, 10);
Add(10.0, 10.0);
Add(10L, 10L);
return 0;
}
通過上面代碼的實現,可以根據具體的Add()的參數去調用對應的函數。
底層的重命名機制將Add函數根據參數的個數,參數的類型,返回值的類型都做了重新命名。那么借助函數重載,一個函數就有多種命名機制。
在C++調用約定(_cdecl 調用約定)中Add函數在底層被解析為:
"int __cdecl Add(int,int)" (?Add@@YAHHH@Z)
"double __cdecl Add(double,double)" (?Add@@YANNN@Z)
"long __cdecl Add(long,long)" (?Add@@YAJJJ@Z)
在C++ 程序中調用被 C 編譯器編譯后的函數,為什么要加 extern “C”聲明?
(1)C++中可以通過在函數聲明前加 extern "C" 將一個函數按照 C 語言的風格來進行編譯。
(2)C++語言支持函數重載。而C不支持函數重載。
(3)函數在C中和C++中編譯過的函數名字是不一樣的。加上extern”C”是說明是說明C已經編譯過的。
C++想要調用已經編譯過的C函數,由於編譯過的名字不同,是不能直接調用的,所以C++加extern“C”生命來解決這個問題。
例如:假設某個函數的原型為: void foo(int x, int y);該函數被C 編譯器編譯后在庫中的名字為_foo, 而C++ 編譯器則會產生像
"int __cdecl Add(int,int)" (?Add@@YAHHH@Z)
"double __cdecl Add(double,double)" (?Add@@YANNN@Z)
"long __cdecl Add(long,long)" (?Add@@YAJJJ@Z)
_foo_int_int 之類的名字,加上extren”C”后,就相當於告訴編譯器,函數foo是個C編譯后的函數,在庫里應該找的是_foo,而不是_foo_int_int.
。
接下來講講怎么通過c語言去實現函數重載
C語言實現函數重載
(1)利用可變參數
但是,在很多情況下,利用可變參數可以實現 C 語言的函數重載的,POSIX 接口中定義的 open 函數就是一個非常好的例子,
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
以下是一個簡單的例子,”重載”了兩個函數,第一個函數是兩個參數,第二個函數帶了三個函數,其中第三個函數是可選的,
ANSI C 標准中,有可變參數的概念,可以通過一組宏實現
函數 描述
col 3 is right-aligned
va_list arg_ptr 定義一個可變參數列表指針
va_start(arg_ptr, argN) 讓arg_ptr指向參數argN
va_arg(arg_ptr, type) 返回類型為type的參數指針,並指向下一個參數
va_copy(dest, src) 拷貝參數列表指針,src->dest,
va_end(arg_ptr) 清空參數列表,並置參數指針arg_ptr無效。每個va_start()必須與一個va_end()對應
#include<stdio.h>
#include<stdarg.h>
int getMax(int n, ...)
{
va_list va;
va_start(va,n); // init va, pointing to the first argument
int tmp,smax=-1;
int i;
for(i=0;i<n;i++)
{
tmp=va_arg(va,int); // get the next argument, the type is int
if(tmp>smax) smax=tmp;
}
va_end(va);
return smax;
}
int main()
{
printf("%d/n",getMax(4,9,5,2,19));
printf("%d/n",getMax(6,1,3,4,5,2,0));
}
參數的內存存放格式:參數存放在內存的堆棧段中,在執行函數的時候,從最后一個開始入棧
因此,假設定義一個可變參數的函數 void f(int x, …), 通過f( x, y, z) 調用,那么,z先入棧,然后y, 然后x。 因此我們只要得到任何一個變量的地址,就可以找到其它變量的地址。
va_start(va, n) 就是讓va指向n的地址。這樣,后面就可以得到所有參數的值。前提是,我們必須知道每個參數的類型。在本例子中,都是int類型。函數指針實現的參數重載(這個是重點,要掌握)
#include<stdio.h>
void func_int(void * a)
{
printf("%d\n",*(int*)a); //輸出int類型,注意 void * 轉化為int
}
void func_double(void * b)
{
printf("%.2f\n",*(double*)b);
}
typedef void (*ptr)(void *); //typedef申明一個函數指針
void c_func(ptr p,void *param)
{
p(param); //調用對應函數
}
int main()
{
int a = 23;
double b = 23.23;
c_func(func_int,&a);
c_func(func_double,&b);
return 0;
}
(3)實現參數類型的重載
這主要是利用了 GCC 的內置函數,__builtin_types_compatible_p()和__builtin_choose_expr(),
例如:
struct s1
{
int a;
int b;
double c;
};
struct s2
{
long long a;
long long b;
};
void gcc_overload_s1(struct s1 s)
{
printf("Got a struct s1: %d %d %f\n", s.a, s.b, s.c);
}
void gcc_overload_s2(struct s2 s)
{
printf("Got a struct s2: %lld %lld\n", s.a, s.b);
}
// warning: dereferencing type-punned pointer will break strict-aliasing rules
#define gcc_overload(A)\
__builtin_choose_expr(__builtin_types_compatible_p(typeof(A), struct s1),\
gcc_overload_s1(*(struct s1 *)&A),\
__builtin_choose_expr(__builtin_types_compatible_p(typeof(A), struct s2),\
gcc_overload_s2(*(struct s2 *)&A),(void)0))
或者一個更高級的寫法:
void gcc_type_overload_aux(int typeval, ...)
{
switch(typeval)
{
case 1:
{
va_list v;
va_start(v, typeval);
struct s1 s = va_arg(v, struct s1);
va_end(v);
gcc_overload_s1(s);
break;
}
case 2:
{
va_list v;
va_start(v, typeval);
struct s2 s = va_arg(v, struct s2);
va_end(v);
gcc_overload_s2(s);
break;
}
default:
{
printf("Invalid type to 'gcc_type_overload()'\n");
exit(1);
}
}
}
#define gcc_type_overload(A)\
gcc_type_overload_aux(\
__builtin_types_compatible_p(typeof(A), struct s1) * 1\
+ __builtin_types_compatible_p(typeof(A), struct s2) * 2\
, A)
轉自:https://blog.csdn.net/ypshowm/java/article/details/89096042