ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

c++之函数与函数重载

2022-03-20 11:01:09  阅读:211  来源: 互联网

标签:函数 cfi int esp c++ ebp C++ 重载


一、函数默认参数


1.1 函数默认参数简介

在C++中,定义函数时可以给形参指定一个默认的值,这样调用函数时如果没有给这个形参赋值(没有对应的实参),那么就使用这个默认的值。

比如:我们要实现一个 分页 插件,在使用时可以由用户指定当前是第几页以及每页显示多少条数据。
如果用户没有指定每页显示多少条默认1页显示10条,打印出每一页显示的信息的下标。

#include<iostream>

using namespace std;

void pagination(int page,int pagesize=10)
{
    int i;
    for(i=0;i<pagesize;i++)
    {
        cout << (page-1)*10 + i << " ";
     }
     cout << endl;
}

int main()
{
    pagination(1);
    pagination(2,15);
    return 0;
}


注意:C++规定,默认参数只能放在形参列表的最后,而且一旦为某个形参指定了默认值,那么它后面的所有形参都必须有默认值。


因为实参和形参的传值是从左到右依次匹配的,默认参数的连续性是保证正确传参的前提。

1.2 指定函数默认参数的位置


C++ 规定,在给定的作用域中只能指定一次默认参数。


例如下面代码:

#include <iostream>
using namespace std;
void func(int a, int b = 10, int c = 36);
int main(){
    func(99);
    return 0;
}
void func(int a, int b = 10, int c = 36){
    cout<<a<<", "<<b<<", "<<c<<endl;
}


编译时代码会报错,因为func() 的定义和声明位于同一个源文件,它们的作用域也都是整个源文件,这样就导致在同一个文件作用域中指定了两次默认参数,违反了 C++ 的规定。

针对于函数的定义和声明在同一作用域内的默认值参数,要么设置在定义处,要么设置在声明处,二选一。

在多文件编程时,C++允许将函数的声明放在主文件中,将函数实现放在其他文件中。


比如:Main.Cpp

#include <iostream>
using namespace std;
void func(int a, int b = 10, int c = 36);
int main(){
    func(99);
    return 0;
}
Moudle.cpp中:
#include <iostream>
using namespace std;
void func(int a, int b = 10, int c = 36){
    cout<<a<<", "<<b<<", "<<c<<endl;
}


上述代码运行时没有问题的,因为func的声明和定义的作用域一个在main.cpp中一个在moudle.cpp中,相互之间不影响。

在使用中建议在函数声明处指定函数默认参数值。

二、*函数占位参数


2.1 占位函数简介


1、C++在声明函数时,可以设置占位参数。占位参数只有参数类型声明,而没有参数名声明。一般情况下,在函数体内部无法使用占位参数。


2、C++中函数的参数列表中可以实现占位参数,调用函数时,必须把该位置进行补全。
示例:占位参数

#include <iostream>   //C++标准头文件不含有.h
#include <stdio.h>
using namespace std;
void fun(int A,int B,int)
{
    cout<<A<<"   "<<B<<"    "<<endl;
}
int main()
{
    fun(1,2,10);
    return 0;
}


2.2 占位参数应用


在重置运算符++时,前置++ 使用operator++()和后置++重载 使用operator++(int),期中函数中参数是没有任何意义的,它的存在只是为了区分是前置形式还是后置形式。
 

// 前置++ 进行重载
Test operator++()
    {
        this->a += 1;
        this->b += 1;

        return *this;
    }

    // 后置++ 进行重载
    Test operator++(int)
    {
        Test temp = *this;

        // this指向的数据进行改变;
        this->a += 1;
        this->b += 1;

        return temp;
    }

三、函数重载及原理


3.1 函数重载简介


在实际开发中,有时候我们需要实现几个功能类似的函数,只是有些细节不同。

例如希望交换两个变量的值,这两个变量有多种类型,可以是 int、float、char等,我们需要通过参数把变量的地址传入函数内部。

在C语言中,程序员往往需要根据不同的灵分别设计出三个不同名的函数,如下:

void swap1(int *a, int *b);      //交换 int 变量的值
void swap2(float *a, float *b);  //交换 float 变量的值
void swap3(char *a, char *b);    //交换 char 变量的值

但是C++ 允许多个函数拥有相同的名字,只要它们的参数列表不同就可以,这就是函数的重载(Function Overloading)。借助重载,一个函数名可以处理不同数据类型的相同处理逻辑。

参数列表包括参数的类型、参数的个数和参数的顺序,只要有一个不同就叫做参数列表不同。

上述代码在C++中可以实现如下:

#include <iostream>
using namespace std;
//交换 int 变量的值
void Swap(int *a, int *b){
    int temp = *a;
    *a = *b;
    *b = temp;
}
//交换 float 变量的值
void Swap(float *a, float *b){
    float temp = *a;
    *a = *b;
    *b = temp;
}
//交换 char 变量的值
void Swap(char *a, char *b){
    char temp = *a;
    *a = *b;
    *b = temp;
}

重载就是在一个作用范围内(同一个类、同一个命名空间等)有多个名称相同但参数不同的函数。重载的结果是让一个函数名拥有了多种用途,使得命名更加方便(在中大型项目中,给变量、函数、类起名字是一件让人苦恼的问题),调用更加灵活。

3.2 函数重载的规则


1、函数名称必须相同。
2、参数列表必须不同(个数不同、类型不同、参数排列顺序不同等)。
3、函数的返回类型可以相同也可以不相同。
4、仅仅返回类型不同不足以成为函数的重载。

3.3 函数重载原理


分别将C语言和C++语言的源代码编译为汇编代码:

#include <stdio.h>

int  add(int A,int B)

{

return A+B;

}

float  add(float A,float B)

{

return A+B;

}

int main()

{

add(10,10);

return 0;

}

#include <iostream>   //C++标准头文件不含有.h

#include <stdio.h>

using namespace std;

int  add(int A,int B)

{

return A+B;

}

float  add(float A,float B)

{

return A+B;

}

double  add(double A,double B)

{

return A+B;

}

int main()

{

cout<<add(10.0,10.0)<<endl;;

return 0;

}

编译器采用GNU计划中的gcc编译器

编译器采用GNU计划中的g++编译器

gcc 1.c -o app

g++ 1.cpp -o app

gcc编译器选项参数

编译:预处理、编译、汇编、链接

预处理:

替换#include <stdio.h>  #define

gcc -E xxx.c -o xxx.i

编译:由C语言编译为汇编指令

gcc -S  xxx.i  -o  xxx.s

汇编:将汇编指令编译为机器指令01

gcc -c 1.s -o 1.o

只当前源文件进行编译;

改变的进行编译;

全部重新编译;

gpio.o lcd.o key.o

链接

gcc bc.o add.o sub.o -o app

参数:

-o 对生成文件进行重命名./重命名

-I指定头文件目录

g++ -S xxx.cpp -o xxx.s

在命令行中,将当前代码编译为汇编代码查看如下:

CC++

.file "1.c"

.text

.globl add

.type add, @function

add:

.LFB0:

.cfi_startproc

pushl %ebp

.cfi_def_cfa_offset 8

.cfi_offset 5, -8

movl %esp, %ebp

.cfi_def_cfa_register 5

movl 8(%ebp), %edx

movl 12(%ebp), %eax

addl %edx, %eax

popl %ebp

.cfi_restore 5

.cfi_def_cfa 4, 4

ret

.cfi_endproc

.LFE0:

.size add, .-add

.globl main

.type main, @function

main:

.LFB1:

.cfi_startproc

pushl %ebp

.cfi_def_cfa_offset 8

.cfi_offset 5, -8

movl %esp, %ebp

.cfi_def_cfa_register 5

pushl $10

pushl $10

call add

addl $8, %esp

movl $0, %eax

leave

.cfi_restore 5

.cfi_def_cfa 4, 4

ret

.cfi_endproc

.LFE1:

.size main, .-main

.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"

.section .note.GNU-stack,"",@progbits

.file "1.cpp"

.local _ZStL8__ioinit

.comm _ZStL8__ioinit,1,1

.text

.globl _Z3addii

.type _Z3addii, @function

_Z3addii:

.LFB1021:

.cfi_startproc

pushl %ebp

.cfi_def_cfa_offset 8

.cfi_offset 5, -8

movl %esp, %ebp

.cfi_def_cfa_register 5

movl 8(%ebp), %edx

movl 12(%ebp), %eax

addl %edx, %eax

popl %ebp

.cfi_restore 5

.cfi_def_cfa 4, 4

ret

.cfi_endproc

.LFE1021:

.size _Z3addii, .-_Z3addii

.globl _Z3addff

.type _Z3addff, @function

_Z3addff:

.LFB1022:

.cfi_startproc

pushl %ebp

.cfi_def_cfa_offset 8

.cfi_offset 5, -8

movl %esp, %ebp

.cfi_def_cfa_register 5

flds 8(%ebp)

fadds 12(%ebp)

popl %ebp

.cfi_restore 5

.cfi_def_cfa 4, 4

ret

.cfi_endproc

.LFE1022:

.size _Z3addff, .-_Z3addff

.globl _Z3adddd

.type _Z3adddd, @function

_Z3adddd:

.LFB1023:

.cfi_startproc

pushl %ebp

.cfi_def_cfa_offset 8

.cfi_offset 5, -8

movl %esp, %ebp

.cfi_def_cfa_register 5

subl $16, %esp

movl 8(%ebp), %eax

movl %eax, -8(%ebp)

movl 12(%ebp), %eax

movl %eax, -4(%ebp)

movl 16(%ebp), %eax

movl %eax, -16(%ebp)

movl 20(%ebp), %eax

movl %eax, -12(%ebp)

fldl -8(%ebp)

faddl -16(%ebp)

leave

.cfi_restore 5

.cfi_def_cfa 4, 4

ret

.cfi_endproc

.LFE1023:

.size _Z3adddd, .-_Z3adddd

.globl main

.type main, @function

main:

.LFB1024:

.cfi_startproc

leal 4(%esp), %ecx

.cfi_def_cfa 1, 0

andl $-16, %esp

pushl -4(%ecx)

pushl %ebp

.cfi_escape 0x10,0x5,0x2,0x75,0

movl %esp, %ebp

pushl %ecx

.cfi_escape 0xf,0x3,0x75,0x7c,0x6

subl $4, %esp

fldl .LC1

leal -8(%esp), %esp

fstpl (%esp)

fldl .LC2

leal -8(%esp), %esp

fstpl (%esp)

call _Z3adddd

addl $16, %esp

subl $4, %esp

leal -8(%esp), %esp

fstpl (%esp)

pushl $_ZSt4cout

call _ZNSolsEd

addl $16, %esp

subl $8, %esp

pushl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_

pushl %eax

call _ZNSolsEPFRSoS_E

addl $16, %esp

movl $0, %eax

movl -4(%ebp), %ecx

.cfi_def_cfa 1, 0

leave

.cfi_restore 5

leal -4(%ecx), %esp

.cfi_def_cfa 4, 4

ret

.cfi_endproc

.LFE1024:

.size main, .-main

.type _Z41__static_initialization_and_destruction_0ii, @function

_Z41__static_initialization_and_destruction_0ii:

.LFB1031:

.cfi_startproc

pushl %ebp

.cfi_def_cfa_offset 8

.cfi_offset 5, -8

movl %esp, %ebp

.cfi_def_cfa_register 5

subl $8, %esp

cmpl $1, 8(%ebp)

jne .L11

cmpl $65535, 12(%ebp)

jne .L11

subl $12, %esp

pushl $_ZStL8__ioinit

call _ZNSt8ios_base4InitC1Ev

addl $16, %esp

subl $4, %esp

pushl $__dso_handle

pushl $_ZStL8__ioinit

pushl $_ZNSt8ios_base4InitD1Ev

call __cxa_atexit

addl $16, %esp

.L11:

nop

leave

.cfi_restore 5

.cfi_def_cfa 4, 4

ret

.cfi_endproc

.LFE1031:

.size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii

.type _GLOBAL__sub_I__Z3addii, @function

_GLOBAL__sub_I__Z3addii:

.LFB1032:

.cfi_startproc

pushl %ebp

.cfi_def_cfa_offset 8

.cfi_offset 5, -8

movl %esp, %ebp

.cfi_def_cfa_register 5

subl $8, %esp

subl $8, %esp

pushl $65535

pushl $1

call _Z41__static_initialization_and_destruction_0ii

addl $16, %esp

leave

.cfi_restore 5

.cfi_def_cfa 4, 4

ret

.cfi_endproc

.LFE1032:

.size _GLOBAL__sub_I__Z3addii, .-_GLOBAL__sub_I__Z3addii

.section .init_array,"aw"

.align 4

.long _GLOBAL__sub_I__Z3addii

.section .rodata

.align 8

.LC1:

.long 858993459

.long 1076114227

.align 8

.LC2:

.long 0

.long 1076101120

.hidden __dso_handle

.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"

.section .note.GNU-stack,"",@progbits

由图可知:C++代码在编译时会根据参数列表对函数进行重命名,例如int add(int a, int b)会被重命名为_Z3addii,float add(float x, float y)会被重命名为_Z3addff。当发生函数调用时,编译器会根据传入的实参去逐个匹配,以选择对应的函数,如果匹配失败,编译器就会报错,这叫做重载决议(Overload Resolution)。

函数重载仅仅是语法层面的,本质上它们还是不同的函数,占用不同的内存,入口地址也不一样。

3.4 实现C++和C语言的混合编程


在 C++ 出现之前,很多实用的功能都是用 C 语言开发的,很多底层的库也是用 C 语言编写的。这意味着,如果能在 C++ 代码中兼容 C 语言代码,无疑能极大地提高 C++ 程序员的开发效率。

C++ 和 C 可以进行混合编程。但需要注意的是,由于 C++ 和 C 在程序的编译、链接等方面都存在一定的差异,而这些差异往往会导致程序运行失败。

例如:

//myfun.h
void display();
//myfun.c
#include <stdio.h>
#include "myfun.h"
void display(){
   printf("C语言:myfunc函数");
}
//main.cpp
#include <iostream>
#include "myfun.h"
using namespace std;
int main(){
   display();
   return 0;
}

主程序是cpp,display是c语言代码,运行时报错:

In function `main': undefined reference to `display()'


C 语言是不支持函数重载的,它不会在编译阶段对函数的名称做较大的改动。
C++支持函数重载,在程序的编译阶段对函数的函数名进行“再次重命名”。

因此使用 C 和 C++ 进行混合编程时,考虑到对函数名的处理方式不同,势必会造成编译器在程序链接阶段无法找到函数具体的实现,导致链接失败。

借助 extern "C",就可以轻松解决 C++ 和 C 在处理代码方式上的差异性。

extern "C" 既可以修饰一句 C++ 代码,也可以修饰一段 C++ 代码,它的功能是让编译器以处理 C 语言代码的方式来处理修饰的 C++ 代码。

为了避免 display() 函数以不同的编译方式处理,我们以如下的方式修改 myfun.h

#ifdef __cplusplus
extern "C" void display();
#else
void display();
#endif


那么当 myfun.h 被引入到 C++ 程序中时,会选择带有 extern "C" 修饰的 display() 函数;反之如果 myfun.h 被引入到 C 语言程序中,则会选择不带 extern "C" 修饰的 display() 函数。由此,无论 display() 函数位于 C++ 程序还是 C 语言程序,都保证了 display() 函数可以按照 C 语言的标准来处理。

当仅修饰一句 C++ 代码时,直接将其添加到该函数代码的开头即可;如果用于修饰一段 C++ 代码,只需为 extern "C" 添加一对大括号{},并将要修饰的代码囊括到括号内即可。

标签:函数,cfi,int,esp,c++,ebp,C++,重载
来源: https://blog.csdn.net/amyliyanice/article/details/123608939

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有