C++函数重载与重载原理:命名倾轧

一、重载概念与原则:

1、重载概念:

在C语言中,一个函数不能与另一个函数重名,而在C++中,只要一个函数的参数列表与另一个函数的参数列表不完全相同,函数名就可以相同。C++这一特点就是所谓函数的重载现象。

同一个名字因为参数列表不同,展现了不同的结果,也叫静多态。

2、重载原则:

①函数名相同,函数参数列表不同(类型、个数、顺序)
②匹配原则1:严格匹配,找到再调用
③匹配原则2:通过隐式类型转换寻求一个匹配,找到则调用
④返回值类型不构成重载条件

/*要是放在C语言中,这段代码必然是编译不通过,而在C++中是合乎重载原则的*/
#include <iostream>

using namespace std;

float abs(float i){
    return (i >= 0 ? i : -i);
}

double abs(double i){
    return (i >= 0 ? i : -i);
}

int abs(int i){
    return (i >= 0 ? i : -i);
}

int main()
{
    /*abbiguous二义性*/
    float i = abs(-5.5f);/*默认调用第一个abs*/
    float j = abs(-4.4);/*默认调用第二个,如果注销掉第二个abs,编译时出错报二义性错误*/
    cout << i << endl;
    cout << j << endl;

    /*浮点数默认大小(类型)*/
    cout << "sizeof(4.5)=" << sizeof(4.5) << endl;/*default*/
    cout << "sizeof(4.5f)=" << sizeof(4.5f) << endl;
    return 0;
}

注意:
重载时的二义性:如果计算机存在有两种隐式转换选择,计算机不会去选,而报二义性错误
eg1:double可以隐式转换为float或int,如果abs(-4.4)并且定义float与int分别作为参数的ads(),编译时则会产生二义性
eg2:double->int/float会产生两义性,int->long/double也会产生两义性。
即两个特例重载时需要注意。 为了避免重载中的二义性问题,使用时按所需强制转换,不要让计算机去自己选择

二、重载原理:命名倾轧(name mangling)

1、重载原理:name mangling(命名倾轧):

对于下面这段程序来说:

#include <iostream>
using namespace std;

//不设置,C++编译器默认倾轧
void func(int a){cout<<"a = "<<a<<endl;}
void func(char a){cout<<"a = "<<a<<endl;}
void func(int a, char b){cout<<"a = "<<a<<endl<<"b = "<<b<<endl;}
void func(char a, int b){cout<<"a = "<<a<<endl<<"b = "<<b<<endl;}

int main(void){
    int a = 10;
    char b = 'b';

    func(a);
    func(b);
    func(a,b);
    func(b,a);

    return 0;
}

上面的程序在经过C++编译器编译时就类似于变成了下面这种写法,这种写法与其重载函数名以及参数类型有关:

#include <iostream>

using namespace std;
/*定义自动倾轧*/
void func_i(int a){cout<<"a = "<<a<<endl;}
void func_c(char a){cout<<"a = "<<a<<endl;}
void func_ic(int a, char b){cout<<"a = "<<a<<endl<<"b = "<<b<<endl;}
void func_ci(char a, int b){cout<<"a = "<<a<<endl<<"b = "<<b<<endl;}

int main(void){
    int a = 10;
    char b = 'b';
    /*调用也自动倾轧*/
    func_i(a);
    func_c(b);
    func_ic(a,b);
    func_ci(b,a);

    return 0;
}

2、C++在兼容C时的不倾轧操作:

在C++中,定义阶段与使用操作阶段均会进行倾轧(编译时倾轧),使用extern “C”,可以将某函数设置为不倾轧,可C++需要倾轧以支持重载,为什么弄一个不倾轧出来呢?

分析:
首先,函数定义阶段与使用要么都倾轧,要么都不倾轧,必须得一一对应,否则会报函数找不到的错误。倾轧是编译时进行的,而对于C++要兼容C的问题,C++就必须兼容C的语法与C库(链接库),C库只在连链接时加入,不存在让C++编译器去倾轧的问题;而C头文件中声明的C库函数在调用时会倾轧,要想使用不参加倾轧的C库函数,C++中编译时就不能倾轧C的头文件中对于库函数的声明,即C库中已经不能修改不倾轧为倾轧,则必须将头文件中的声明也设置为不倾轧,以此与库中相互对应。如果查看C的标准头文件,可以发现其中有一个extern “C”,表示不倾轧调用时的函数名。如下是string.h头文件中的一部分:

/*查看string.h,发现在函数声明之前,就对C++编译器的编译方式进行声明extern "C",即声明为:C++编译器在编译时遇到C语言库对应的调用的函数名不倾轧*/

#ifndef _INC_STRING
#define _INC_STRING

#ifdef __cplusplus  //如果是C++编译器就要进行不倾轧设置
extern "C" {
#endif

... //函数声明等

#ifdef __cplusplus//与上面匹配
}
#endif
...

分别查看不同集成承环境中的string.h文件,都是有对函数的extern “C”不倾轧限定:

这里写图片描述

举例说明:

#include <iostream>
#include <string.h>
using namespace std;

extern "C"{
    void func(int a){cout<<"a = "<<a<<endl;}
    void func(int a,double b){cout<<"a = "<<a<<endl;}
}

int main(void){
    int a = 10;

    func(a);
    return 0;
}

这样的代码是不能够完成编译的(因为不倾轧就会使得两个函数名一样,产生重名,而重载也是不重名的)。而要上面的函数能够正常调用,定义与调用时均遵循默认的倾轧(而且必须是倾轧,否则会产生二义性),做法如下:

#include <iostream>
#include <string.h>
using namespace std;
//倾轧就不会重名
void func(int a){cout<<"a = "<<a<<endl;}
void func(int a,double b){cout<<"a = "<<a<<endl;}

int main(void){
    int a = 10;

    func(a);
    return 0;
}

再用一个例子测试:

/*main.cpp*/
#include<iostream>
#include"func.h"

using namespace std;

int main(void){
    int a = 10;

    func(a);

    return 0;
}
/*func.c*/
#include<iostream>
using namespace std;
//定义倾轧
extern "C"{
    void func(int a){cout<<"a = "<<a<<endl;}
}
/*func.h*/
#ifndef FUNC_H_
#define FUNC_H_

extern "C" void func(int);//声明为不倾轧

#endif

测试结果如下:

声明中不加extern “C”,但定义中加extern “C”,编译出错:
这里写图片描述
声明中加extern “C”,定义中也加extern “C”,编译不出错:
这里写图片描述

总之一句话,倾轧是C++为了实现函数重载而设计的,不倾轧的extern “C”则是为了兼容C而后实现的。我们编程一般犯不着对自定义的C++函数设置。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
C++函数重载需要满足以下条件: 1. 函数名称相同:函数重载是指在同一个作用域内,存在多个函数名称相同但参数列表不同的函数。 2. 参数列表不同:函数重载的关键在于参数列表的不同。参数列表可以包括参数的类型、参数的个数、参数的顺序等。 3. 返回类型可以相同也可以不同:函数重载的返回类型可以相同,也可以不同。只有返回类型不同而参数列表相同的函数不能构成重载。 4. 函数重载不能仅仅依靠返回类型的差异:如果只是返回类型不同而参数列表相同,这种情况下编译器无法确定具体调用哪个函数,因此不能构成重载。 5. 函数重载可以发生在类内部或全局作用域内:函数重载可以在类内部进行,也可以在全局作用域内进行。 需要注意的是,函数重载并不仅仅限于参数列表的不同,还可以包括 `const` 修饰符、引用类型等。函数重载使得程序设计更加灵活,可以根据不同的参数类型或参数个数来选择合适的函数进行调用。编译器会根据函数调用时提供的参数匹配最合适的重载函数。 以下是一个示例,展示了函数重载的使用: ```cpp // 在全局作用域内进行函数重载 void print(int num) { std::cout << "Integer: " << num << std::endl; } void print(double num) { std::cout << "Double: " << num << std::endl; } // 在类内部进行函数重载 class MyClass { public: void print(std::string str) { std::cout << "String: " << str << std::endl; } void print(int num) { std::cout << "Integer: " << num << std::endl; } }; int main() { print(10); // 调用全局作用域的 print(int) 函数 print(3.14); // 调用全局作用域的 print(double) 函数 MyClass obj; obj.print("Hello"); // 调用类内部的 print(std::string) 函数 obj.print(20); // 调用类内部的 print(int) 函数 return 0; } ``` 在上述示例中,函数 `print` 在全局作用域和类内部都进行了重载,根据提供的参数类型选择合适的函数进行调用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值