概述
常用的几类函数
#include <iostream>
void func(int a){
std::cout << "global func: " << a << std::endl;
}
namespace test{
void func(int a){
std::cout << "namespace test func: " << a << std::endl;
}
};
class Demo {
public:
void func(int a){
i = 1;
std::cout << "class member func: " << a << std::endl;
}
int i{0};
};
int main()
{
func(1);
test::func(1);
Demo d;
d.func(1);
return 0;
}
函数的重载
对于程序的整个生命周期如下:编写程序(源代码)-> 预处理 -> 编译(扫描->词法分析->语法分析->语义分析->源代码优化->代码生成->目标代码优化) -> 汇编 -> 链接 -> 运行;
编译器将源代码编译产生目标文件时,符号名与相应的变量和函数进行对应,在比较早的时候,两者是一样的,比如汇编源代码中包含一个函数foo,编译成目标文件时,对应的符号名也是foo;C语言的规则比较类似,将全局变量和函数的符号名前加上下划线"_";
比如:
extern "C" {
void func(int a){
std::cout << "global func: " << a << std::endl;
}
}
编译成的符号如下(readelf -s main | grep func):
Num: Value Size Type Bind Vis Ndx Name
60: 00000000000011e9 74 FUNC GLOBAL DEFAULT 16 func
由于C语言的符号生成规则如此,所以不支持函数的重载。也因此只要符号相同(相同的函数名、变量名会编译失败)
那么对于C++,它拥有类、继承、虚机制、重载、名称空间等这些特性,使符号管理(符号生成规则)更为复杂。为了支持这些特性,发明了Name Decoration 或 Name Mangling机制
看如下函数的符号是如何:
int func(int){return 0;};
float func(float){return 0;};
class C{
public:
int func(int){return 0;};
class C2 {
public:
int func(int){return 0;};
};
};
namespace N{
int func(int){return 0;};
class C{
public:
int func(int){return 0;};
};
};
int main(){
func(1);
func(1.0f);
C c;
c.func(1);
C::C2 c2;
c2.func(1);
N::func(1);
N::C c1;
c1.func(1);
return 0;
}
命令:
编译: g++ main.cpp -o main
查看符号:readelf -s main | grep func
统计:
函数签名 | 修饰后名称(符号名) |
---|---|
int func(int) | _Z4funci |
float func(float) | _Z4funcf |
int C::func(int) | _ZN1C4funcEi |
int C::C2::func(int) | _ZN1C2C24funcEi |
int N::func(int) | _ZN1N4funcEi |
int N::C::func(int) | _ZN1N1C4funcE |
基本规则如下:
所有符号以_Z开头,对于嵌套的名字(名称空间或类里面),后面紧跟着“N”, 然后是各个名称空间和类的名字,每个名字前是名字字符串长度,再以E结尾,对于函数来说,它的参数列表紧跟在“E”后面,对于int类型来说,就是字母i。
<type> ::= <builtin-type>
::= <qualified-type>
::= <function-type>
::= <class-enum-type>
::= <array-type>
::= <pointer-to-member-type>
::= <template-param>
::= <template-template-param> <template-args>
::= <decltype>
::= P <type> # pointer
::= R <type> # l-value reference
::= O <type> # r-value reference (C++11)
::= C <type> # complex pair (C99)
::= G <type> # imaginary (C99)
::= <substitution> # See Compression below
<CV-qualifiers> ::= [r] [V] [K] # restrict (C99), volatile, const
<builtin-type> ::= v # void
::= w # wchar_t
::= b # bool
::= c # char
::= a # signed char
::= h # unsigned char
::= s # short
::= t # unsigned short
::= i # int
::= j # unsigned int
::= l # long
::= m # unsigned long
::= x # long long, __int64
::= y # unsigned long long, __int64
::= n # __int128
::= o # unsigned __int128
::= f # float
::= d # double
::= e # long double, __float80
::= g # __float128
::= z # ellipsis
::= Dd # IEEE 754r decimal floating point (64 bits)
::= De # IEEE 754r decimal floating point (128 bits)
::= Df # IEEE 754r decimal floating point (32 bits)
::= Dh # IEEE 754r half-precision floating point (16 bits)
::= DF <number> _ # ISO/IEC TS 18661 binary floating point type _FloatN (N bits)
::= DB <number> _ # C23 signed _BitInt(N)
::= DB <instantiation-dependent expression> _ # C23 signed _BitInt(N)
::= DU <number> _ # C23 unsigned _BitInt(N)
::= DU <instantiation-dependent expression> _ # C23 unsigned _BitInt(N)
::= Di # char32_t
::= Ds # char16_t
::= Du # char8_t
::= Da # auto
::= Dc # decltype(auto)
::= Dn # std::nullptr_t (i.e., decltype(nullptr))
::= u <source-name> [<template-args>] # vendor extended type
函数的名称修饰是否包含返回值,有如下解释:
Whether the mangling of a function type includes the return type depends on the context and the nature of the function. The rules for deciding whether the return type is included are:
- Template functions (names or types) have return types encoded, with the exceptions listed below.
- Function types not appearing as part of a function name mangling, e.g. parameters, pointer types, etc., have return type encoded, with the exceptions listed below.
- Non-template function names do not have return types encoded.
The exceptions mentioned in (1) and (2) above, for which the return type is never included, are
- Constructors.
- Destructors.
- Conversion operator functions, e.g. operator int.
const重载
先看下面几个例子:
1:
void func(int a){
std::cout << "global func: " << a << std::endl;
}
void func(const int a){
std::cout << "global func: " << a << std::endl;
}
和2:
void func(int& a){
std::cout << "global func: " << a << std::endl;
}
void func(const int& a){
std::cout << "global func: " << a << std::endl;
}
先猜测下上述两个例子表现如何?
结论:例1编译失败(error: redefinition of ‘void func1(int)’ 相同的函数符号相同),例2编译成功;为什么呢?
从编译出的符号进行倒推:
函数原型 | 符号 |
---|---|
void func(int) | _Z4funci |
void func(const int) | _Z4funci |
void func(int&) | _Z4funcRi |
void func(const int&) | _Z4funcRKi |
由于void func(int) 和void func(const int) 生成的符号相同,那么必然会编译失败;其思想在于,当函数参数是基本的value时,都是将实参拷贝一份然后传入函数,那么const与否是不会影响到调用方,所以无需区分这两种情况(编译器规则)。
再看一个例子:
class Demo {
public:
void func(int a){
i = 1;
std::cout << "class member func: " << a << std::endl;
}
void func(int a) const {
// i = 1; // compile fail
std::cout << "class member const func: " << a << std::endl;
}
int i{0};
};
编译ok;
函数原型 | 符号 |
---|---|
void const Demo::func(int) | _ZNK4Demo4funcEi |
void Demo::func(int) | _ZN4Demo4funcEi |
第二个成员函数的const是修饰Demo object的。
const修饰返回值时,由于“Non-template function names do not have return types encoded”;对于这种情况,返回值使用和不使用const修饰生成的符号是相同的,若同时存在则会编译失败。StackOverflow 解答
最后
以上就是高高外套为你收集整理的从几个例子看函数重载常用的几类函数函数的重载const重载的全部内容,希望文章能够帮你解决从几个例子看函数重载常用的几类函数函数的重载const重载所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复