你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C++中的函数重载及进阶学习

[复制链接]
gaosmile 发布时间:2020-8-10 22:17
一、C++中的函数重载:
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()
{

   return 0;

}

上面在c++编译器里面编译时没有问题的,如果放在c语言编译器里面编译是会报错的:
root@txp-virtual-machine:/home/txp# gcc test5.c
test5.c:8:5: error: conflicting types for ‘func’
int func(int a, int b)
     ^
test5.c:3:5: note: previous definition of ‘func’ was here
int func(int x)
     ^
test5.c:13:5: error: conflicting types for ‘func’
int func(const char* s)
     ^
test5.c:3:5: note: previous definition of ‘func’ was here
int func(int x)

所以说c语言里面不支持函数重载。
2、函数重载至少要满足下面的一个条件:
  • 参数个数不同
  • 参数类型不同
  • 参数顺序不同

比如下面两个函数可以构造重载函数吗?
int func (int a,const char* s)
{
   return a;
}

int func(const char*s,int a)
{
    return strlen(s)
}

答案肯定是可以构造重载函数的,读者可以自己试试(这个比较好理解)。
3、当函数默认参数遇上函数重载会发生什么?
例如下面的两个函数:
int func(int a, int b, int c =0)
{
    return a*b*c;
}
int func(int a, int b)
{
   return a+b;
}

到底会发生啥,我们还是看下面这个实验:
#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 argc, char *argv[])
{
    int c = func(1, 2);
   
    return 0;
}

运行结果:
root@txp-virtual-machine:/home/txp# g++ test5.cpp
test5.cpp: In function ‘int main(int, char**)’:
test5.cpp:16:22: error: call of overloaded ‘func(int, int)’ is ambiguous
     int c = func(1, 2);
                      ^
test5.cpp:16:22: note: candidates are:
test5.cpp:3:5: note: int func(int, int, int)
int func(int a, int b, int c = 0)
     ^
test5.cpp:8:5: note: int func(int, int)
int func(int a, int b)

从上面报错的结果里面有一个单词ambiguous(意思是梦棱两可的),也就是说默认参数这种使用时不允许的。
4、C++编译器调用重载函数的准则:
  • 将所有同名函数作为候选者
  • 尝试寻找可行的候选函数:
  • 精确匹配实参

       通过默认参数能够匹配实参
      通过默认类型转换匹配实参
  • 匹配失败:

      最终寻找到的候选函数不唯一,则出现二义性,编译失败
      无法匹配所有候选者,函数未定义编译失败
5、函数重载的注意事项:
  • 重载函数在本质上是相互独立的不同函数
  • 重载函数的函数类型不同
  • 函数返回值不能作为函数重载的依据
  • 函数重载是由函数名和参数列表决定的

代码测试:
#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;
}

运行结果:
root@txp-virtual-machine:/home/txp# ./a.out
0x40052d
0x400541

从输出结果我们可以看出这两个函数的入口地址不一样,这表明这两个函数是不同的函数。
6、小结:
  • 函数重载是c++中引入的概念
  • 函数重载的本质是相互独立的不同函数
  • c++中通过函数名和函数参数确定函数调用

二、重载函数进阶学习
1、重载与指针:
下面的函数指针将保存哪个函数的地址?
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 c =0;

PFUNC p = func;

c = p(2)//到底选择哪个func函数

函数重载遇上函数指针:
  • 将函数名赋值给函数指针时
  • 根据重载规则跳线与函数指针参数列表一致的候选者
  • 严格匹配候选者的函数类型与函数指针的函数类型

代码试验:
#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(2);
     printf("c=%d\n",c);
     return 0;
}

输出结果:
root@txp-virtual-machine:/home/txp# ./a.out
c=2

从输出结果来看,很明显调用了第一个func函数。
2、注意:
  • 函数重载必然发生在同一个作用域中
  • 编译器需要用参数列表或者函数类型进行函数选择(也就是说碰到指针,要注意函数类型了)

无法直接通过函数名得到重载函数的入口地址,这里还是通过上面的例子演示一下:
#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", add);
    printf("%p\n", add);

    return 0;
}

输出结果:
root@txp-virtual-machine:/home/txp# g++ test5.cpp
test5.cpp: In function ‘int main()’:
test5.cpp:15:23: error: overloaded function with no contextual type information
     printf("%p\n", add);
                       ^
test5.cpp:16:23: error: overloaded function with no contextual type information
     printf("%p\n", add);

三、C++和C相互调用:
  • 实际工程中C++和c代码相互调用是不可避免的
  • c++编译器能够兼容c语言的编译方式
  • c++编译器会优先使用c++编译的方式

extern关键字能够强制让C++编译器进行c方式的编译:
extern "c"
{


}

1、下面进行一个c++中调用c函数,这里我在当前创建三个文件:add.c 、add.h 、main.cpp。内容分别如下:
add.c内容:

#include "add.h"
int add(int a, int b)

{

    return a + b;

}

add.h内容:
int add(int a, int b);
然后我用gcc编译编译生成add.o文件:
root@txp-virtual-machine:/home/txp/add# vim add.c
root@txp-virtual-machine:/home/txp/add# vim add.h
root@txp-virtual-machine:/home/txp/add# gcc -c add.c -o add.o
root@txp-virtual-machine:/home/txp/add# ls
add.c  add.h  add.o

然后main.cpp里面调用add.c
#include <stdio.h>
int main()
{
    int c = add(1, 2);
   
    printf("c = %d\n", c);
   
    return 0;
}

输出结果:
root@txp-virtual-machine:/home/txp/add# g++ main.cpp add.o
/tmp/ccqz3abQ.o: In function `main':
main.cpp.text+0x13): undefined reference to `add(int, int)'
collect2: error: ld returned 1 exit status


结果显示找不到这个函数,为了能够在c++里面调用c语言里面的函数,我们就要使用刚才上面讲的第四点了;这里我们先用nm命令来查看一下add.o文件里面是否生成符号表(有生成):
root@txp-virtual-machine:/home/txp/add# nm add.o
0000000000000000 T add

解决方法,main.cpp改成:
#include <stdio.h>

extern "c"
{
    #include "add.h"

}


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

    return 0;
}

输出结果:
root@txp-virtual-machine:/home/txp/add# ./a.out
c = 3

2、c中如何调用c++函数:
这里我把main.cpp的内容改成:
extern "C"
{
   int add(int a, int b);

}

int add(int a, int b)
{
    return a+b;   
}


编译输出:
root@txp-virtual-machine:/home/txp/add# g++ -c main.cpp -o test.o
root@txp-virtual-machine:/home/txp/add# nm -s test.o
0000000000000000 T add


add.c文件内容改成:
#include <stdio.h>
int main()
{
     int c =0;
     c = add(2,3);
     printf("c=%d\n",c);
     return 0;
}

输出结果:
root@txp-virtual-machine:/home/txp/add# gcc add.c test.o
root@txp-virtual-machine:/home/txp/add# ./a.out
c=5

3、如何保证一段c代码只会以c的方式被编译?
解决方法如下:
  • __cplusplus是c++编译器内置的标准宏定义
  • __cplusplus的意义,确保c代码以统一的c方式被编译成目标文件

#ifdef __cplusplus
extern "C"
{
  #endif

  #ifdef __cplusplus
}
#endif

这里把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;
}

输出结果:
root@txp-virtual-machine:/home/txp/add# g++ main.cpp add.o
root@txp-virtual-machine:/home/txp/add# ./a.out
c = 3

4、注意事项
  • C++编译器不能以c的方式编译重载函数
  • 编译方式决定函数名被编译后的目标名
  • c++编译方式将函数名和参数列表编译成目标名,这里举个例子main.cpp:

int add(int a, int b)
{
  return a+b;
}

int add(int a, int b , int c)
{
   return a+b+c;
}

编译输出:
root@txp-virtual-machine:/home/txp/add# g++ -c main.cpp -o test.oo
root@txp-virtual-machine:/home/txp/add# nm test.oo
0000000000000000 T _Z3addii
0000000000000014 T _Z3addiii

说明ii表示两个参数,iii表示三个参数
  • c编译方式只将函数名作为目标名进行编译,这里还是以main.cpp为例:

extern "C"
{
   int add(int a, int b)
  {
       return a+b;
  }

  int add(int a, int b , int c)
   {
      return a+b+c;
   }
}

输出结果:
root@txp-virtual-machine:/home/txp/add# g++ -c main.cpp -o test.oo
main.cpp: In function ‘int add(int, int, int)’:
main.cpp:8:29: error: declaration of C function ‘int add(int, int, int)’ conflicts with
int add(int a, int b , int c)
                             ^
main.cpp:3:5: error: previous declaration ‘int add(int, int)’ here
int add(int a, int b)

目标名起冲突所以报错。
5、小结:
  • 函数重载是c++对c的一个重要升级
  • 函数重载通过参数列表区分不同的同名函数
  • extern关键字能够实现c和c++的相互调用
  • 编译方式决定符号表中的函数名的最终目标名

四、总结:

好了,今天的分享就到这里,如果文章中有错误或者不理解的地方,可以交流互动,一起进步。

收藏 评论0 发布时间:2020-8-10 22:17

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版