[C++] C++入门第一篇 -- 命名空间,输入输出,缺省函数,函数重载底层原理2

简介: [C++] C++入门第一篇 -- 命名空间,输入输出,缺省函数,函数重载底层原理2

4.2 缺省参数分类

4.2.1 全缺省参数

全缺省参数就是函数的每个形参都有一个默认值,所以在调用函数的时候不用传实参,要传实参的话就必须从左到右依次去传。但是在传参的时候不能跳着传,例如下面:

void func(int a = 10, int b = 20, int c = 30)
{
  cout << a << " " << b << " " << c << endl;
}
int main()
{
  func(1);        //可以传一个
  func(1, 2);     //可以传两个
  func(1, 2, 3);  //可以全部传
  //我们传参是从左到右依次传
  return 0;
}

运行结果:

跳着传参:



C++缺省函数的调用压根就没有跳过传的概念,这样是不可以的。

4.2.2 半缺省参数

半缺省是多个形参一部分给缺省值,但是给缺省值的时候必须从右往左缺省,也是不能跳着缺省。


我们写一个代码来看看半缺省:

//半缺省
void func(int a, int b = 20, int c = 30)
{
  cout << a << " " << b << " " << c << endl;
}
int main()
{
  func(1);        //第一个必须传
  func(1, 2);     //第二个可以传,可以不传
  func(1, 2, 3);  //后两个都是可传,可不传
  //我们传参是从左到右依次传
  return 0;
}

运行结果:

我们可以看到上面的代码中,我们写的func函数的第一个形参a是没有给默认值的,因此在调用的时候第一个参数必须传,不传就会出错。

学会了缺省参数,那它的用途是什么呢?

在栈中,我们使用的时候就已经知道我们要预先开辟1000个空间的时候,就可以使用缺省参数来写。

struct Stack
{
  int* a;
  int size;
  int capacity;
};
void StackInit(Stack* ps, int n = 4)
{
  assert(ps);
  ps->a = (int*)malloc(sizeof(int) * n);
  if (NULL == ps->a)
  {
    perror("malloc fail:");
    return;
  }
  ps->size = 0;
  ps->capacity = n;
}
int main()
{
  Stack st;
  StackInit(&st, 1000);
  return 0;
}

这里我们就用到了半缺省参数,如果我们只是初始化,但是不知道多大空间合适,可以选择不传,初始化的时候只会开4个空间,也不会造成浪费。这就是缺省参数的灵活运用了。

5、函数重载

5.1 函数重载概念

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

a. 我们可以根据形参类型的不同但是函数名相同来调用函数。

int Add(int x, int y)
{
  return x + y;
}
double Add(double x, double y)
{
  return x + y;
}
int main()
{
  cout << Add(1, 2) << endl;
  cout << Add(1.1, 2.2) << endl;
  return 0;
}

运行结果:

上面的代码我们可以看到,虽然它们的名字都是Add,但是参数的类型不同,我们在调用的时候传入的实参会根据类型去调用名字相同但是类型不同的函数,因此类型不同构成重载。

b. 还可以根据形参类型顺序的不同但是函数名相同来调用函数。

void func(int x, double y)
{
  cout << "void func(int x, double y)" << endl;
}
void func(double x, int y)
{
  cout << "void func(double x, int y)" << endl;
}
int main()
{
  func(1, 1.1);
  func(1.1, 1);
  return 0;
}

运行结果:

这里的两个func函数的形参都是一个整型,一个浮点型,但是两个形参的顺序不同,在函数调用的时候,传的实参会根据类型的不用在调用的时候,调用不同的函数,因此参数顺序不同构成重载。

c. 参数个数不同,但函数名相同来调用函数。

void func(int x, double y)
{
  cout << "void func(int x, double y)" << endl;
}
void func(int x, double y, int z)
{
  cout << "void func(int x, double y, int z)" << endl;
}
int main()
{
  func(1, 1.1);
  func(1.1, 1, 2);
  return 0;
}

运行结果:

这里的参数个数不同,第一个函数两个参数,第二个函数三个参数,调用的时候传的参数个数不同,所调用的函数也就不同,因此参数个数不同构成函数重载。

写到这了,肯定会有人想,那返回值类型不同是不是也可以构成重载呢?

我们先回答一下:不可以。我们来验证一下:

short func(short x, short y)
{
  return x + y;
}
int func(short x, short y)
{
  return x + y;
}


这里可以看到,返回类型不同无法构成重载。

总结:函数在名字相同的前提下,参数的类型不同,参数的顺序不同,参数的个数不同,这三个情况下都可以构成函数重载。

5.2 C++支持函数重载的原理--名字修饰(name Mangling)

为什么C++支持函数重载,而C语言不支持函数重载呢?

在C/C++中,一个程序运行起来需要经过四个阶段:预处理,编译,汇编,链接。
这四个阶段的流程分别是(以Linux的流程来说):


1、预处理:头文件展开,宏替换,去掉注释,条件编译,生成test.i


2、编译:检查语法,生成汇编(指令级代码),生成test.s


3、汇编:将汇编代码转为二进制机器码,生成test.o


4、链接:合并链接,生成可执行程序,a.out


C++函数重载主要就是在链接的时候用函数的名字找地址,C语言中用也是如此。但是C++引入了函数名修饰规则,这就是C++支持函数重载的根本原因。我们以Linux环境来展开说。


结合上面的两个截图我们能总结出来,g++下的函数名修饰规则是:

_Z 函数名长度 函数名 形参的首字母

因此函数名就算相同,但是类型不同,顺序不同,个数不同都会产生不同的函数名。

函数调用的时候指令就是call 函数名,而修饰过的函数名跳转后会找到对应的函数的地址,这样就能找到对应的函数。这就是为什么函数名修饰规则修饰后,根据不同的函数名在符号便中找到对应的函数名,也就找到了对应的地址,最终跳转过去,调用对应函数的原因。


C语言不行的原因是没有像C++这样的函数名修饰规则,它的函数名直接存放在符号表中,如果是相同的函数名,一进入符号表就乱了,它不会再加上类型的首字母这样区分函数,因此C语言实现不了函数重载。



相关文章
|
3月前
|
程序员 C++
C++中的函数重载有什么作用
【10月更文挑战第19天】C++中的函数重载有什么作用
32 3
|
3月前
|
编译器 程序员 C++
C++中的函数重载是什么
【10月更文挑战第19天】C++中的函数重载是什么
52 0
|
3月前
|
存储 安全 编译器
【C++打怪之路Lv1】-- 入门二级
【C++打怪之路Lv1】-- 入门二级
39 0
|
3月前
|
自然语言处理 编译器 C语言
【C++打怪之路Lv1】-- C++开篇(入门)
【C++打怪之路Lv1】-- C++开篇(入门)
42 0
|
3月前
|
分布式计算 Java 编译器
【C++入门(下)】—— 我与C++的不解之缘(二)
【C++入门(下)】—— 我与C++的不解之缘(二)
|
3月前
|
编译器 Linux C语言
【C++入门(上)】—— 我与C++的不解之缘(一)
【C++入门(上)】—— 我与C++的不解之缘(一)
|
9天前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
49 18
|
9天前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
34 13
|
9天前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
31 5
|
9天前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
21 5