欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【C++深度解析】6、函数重载

程序员文章站 2024-03-21 16:23:58
...

1 初识重载

函数重载指的是用同一个函数名定义不同的函数,当函数名和不同的参数搭配时函数的含义不同。

编程实验:函数重载初探

// 6-1.c
#include <stdio.h>
#include<string.h>
int func(int x)
{
    return x;
}
int func(int a, int b)
{
    return a + b;
}
int func(const char* s)
{
    return strlen(s);
}
int main(int argc, char *argv[])
{
    printf("%d\n", func(3));
    printf("%d\n", func(3, 4));
    printf("%d\n", func("Hello"));
    return 0;
}

编译运行:

$ g++ 6-1.c -o 6-1
$ ./6-1
3
7
5

可以看出函数名和不同的参数搭配时函数的含义不同,调用的函数不同。实现同一函数名定义不同函数。

1.1 重载条件

函数重载至少满足下面的一个条件:

  • 参数个数不同
  • 参数类型不同
  • 参数顺序不同

下面两个函数的参数顺序不同,所以是重载函数。
【C++深度解析】6、函数重载

1.2 避免默认参数碰到函数重载

当默认参数碰到函数重载会发生什么呢?我们通过实验来说明:

直接看代码,有两个 func() 函数,第一个 func() 函数有三个 int 型参数,第三个参有默认值。第二个 func() 函数有两个 int 型参数。

// 6-2.c
#include<stdio.h>
int func(int a, int b, int c = 0)
{
    return a * b * c;
}
int func(int a, int b)
{
    return a + b;
}
int main()
{
    int c = func(1, 2);
    return 0;
}

编译结果如下:

$ g++ 6-2.c -o 6-2
6-2.c: In function ‘int main()’:
6-2.c:13:22: error: call of overloaded ‘func(int, int)’ is ambiguous
     int c = func(1, 2);
                      ^
6-2.c:3:5: note: candidate: int func(int, int, int)
 int func(int a, int b, int c = 0)
     ^~~~
6-2.c:7:5: note: candidate: int func(int, int)
 int func(int a, int b)
     ^~~~

编译器报错,说 func() 函数调用不明确,两个函数都满足调用条件,编译器不知道调用谁,直接报错。

1.3 编译器调用重载函数的准则

  • 将所有的同名函数作为候选者
  • 尝试寻找可行的候选函数
    • 精确匹配实参
    • 通过默认参数能够匹配实参
    • 通过默认类型转换匹配实参
  • 匹配失败
  • 最终寻找到的候选函数不唯一,有二义性
  • 无法匹配候选者

2 函数重载的本质

函数重载是函数名和参数列表决定的:

  • 重载函数在本质上是相互独立的不同函数,其函数类型不同
  • 函数返回值不能作为函数重载的依据
// 6-3.c
#include<stdio.h>
int add(int a, int b)           // 函数类型:int(int, int)
{
    return a + b;
}
int add(int a, int b, int c)    // 函数类型:int(int, int ,int)
{
    return a + b + c;
}
int main()
{
    printf("%p\n", (int(*)(int, int))add);
    printf("%p\n", (int(*)(int, int, int))add); 
    return 0;
}

上面的代码将两个重载函数的地址打印出来。

$ g++ 6-3.c -o 6-3
$ ./6-3
0x560c03a8c64a
0x560c03a8c65e

可以看出两个重载函数的地址完全不同,说明这是两个独立的函数。

3 重载与指针

将重载函数名赋值给函数指针时:

  • 严格匹配候选者的函数类型与函数指针的函数类型,也就是数需要匹配参数个数,参数类型,参数顺序和返回值类型

直接看代码,下面的函数指针将保存哪个函数的地址呢?

// 6-4.c
#include<stdio.h>
#include<string.h>
int func(int x)
{
    return x;
}
int func(int a, int b)
{
    return a + b;
}
int func(const char* s)
{
    return strlen(s);
}
typedef int(*PFUNC)(int a);
int main(int argc, char* argv[])
{
    int c = 0;
    PFUNC p = func;
    c = p(1);
    printf("c = %d\n", c);
    return 0;
}

定义函数指针 PFUNC,参数类型 int,返回值为 int,则将 PFUNC 指向 函数 func(),将保存那个函数的地址呢?

编译运行:

$ g++ 6-4.c -o 6-4
$ ./6-4
c = 1

可以看出函数指针保存的是函数 int func(int x); 的地址。

如果将代码 6-4.c 的第 16 行改为 typedef void(*PFUNC)(int a); 或者 typedef double(*PFUNC)(int a); 重新编译
【C++深度解析】6、函数重载
编译器报错,没有匹配的函数与函数指针相对应。

typedef void(*PFUNC)(int a); 的函数类型为 void(int, int)
typedef double(*PFUNC)(int a); 的函数类型为 double(int, int)
这两个函数类型与所有的 func() 函数都不匹配。

注意:

  • 函数重载必然发生在同一作用域中
  • 编译器需要用参数列表函数类型进行函数选择
  • 无法直接通过函数名得到重载函数的入口地址

4 C++ 和 C 相互调用

  • C++ 编译器能够兼容 C 语言的编译方式
  • C++ 编译器优先使用 C++ 编译的方式
  • extern 关键字能强制让 C++ 编译器进行 C 方式的编译
extern "C"
{
	// do C-style compilation here
}

编程实验:C++调用 C 函数

// add.c
#include "add.h"
int add(int a, int b)
{
    return a + b;
}
// add.h
int add(int a, int b);

首先将上面的代码用 gcc 编译成汇编器处理之后,链接器处理之前的 .o 文件,生成 add.o 文件。

$ gcc -c add.c -o add.o
// main.cpp
#include<stdio.h>
#include "add.h"
int main()
{
    int c = add(1, 2);
    printf("c = %d\n", c);
    return 0;
}

在 main.cpp 中调用 add.o 中的 add 函数,编译如下:

$ g++ main.cpp add.o -o main
/tmp/cccdww4k.o:在函数‘main’中:
main.cpp:(.text+0x13):对‘add(int, int)’未定义的引用
collect2: error: ld returned 1 exit status

编译器报错,说 add 函数没定义,这是因为 add.o 是以 C 语言的规则编译的,以 C++ 规则找不到编译后的文件名。所以在 C++ 编译器中编译 main.cpp 中的 #include “add.h” 应该以 C 语言的规则进行编译。

解决方案
但是,如何保证一段 C 代码只会以 C 的方式被编译?

  • __cplusplus 是 C++ 编译器内置的标准宏定义,用于确保 C 代码以统一的 C 方式被编译成目标文件

将 main.cpp 更改如下:

// main.cpp
#include<stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

#include "add.h"

#ifdef __cplusplus
}
#endif

int main()
{
    int c = add(1, 2);
    printf("c = %d\n", c);
    return 0;
}

编译运行:

$ g++ main.cpp add.o -o main
$ ./main
c = 3

这样,无论是 C 编译器还是 C++ 编译器,都可以运行。

注意事项:

  • C++ 编译器不能以 C 的方式编译重载函数,C 中没有重载
  • C++编译方式将函数名参数列表编译器目标文件,C 编译方式只将函数名作为目标名进行编译‘

5 小结

1、重载使用一个函数定义不同的函数
2、重载本质为相互独立的不同的函数
3、C++ 通过函数名和函数参数确定函数调用
4、extren 关键字能够实现 C 和 C++ 的相互调用
5、编译方式决定符号表中的函数名的最终目标名

相关标签: C++深度解析