探索C嘎嘎的奇妙世界:第三关---缺省参数与函数重载

在c语言中,我们常常在对有参函数进行传参,这样的繁琐过程,C++祖师爷对此进行了相关改进,多说无益,上干货:

1 缺省参数:

缺省参数 是指在声明或定义函数时为函数的形参指定一个默认值(默认参数)。在调用该函数时,如果没有指定实参,则采用该形参的缺省值;否则使用指定的实参。 缺省参数主要分为两种类型:全缺省参数半缺省参数

1.1 全缺省参数:

全缺省参数是指函数的所有参数都具有默认值。以下是一个全缺省参数的示例代码:

cpp 复制代码
// 全缺省
void F2(int a = 10, int b = 20, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}

int main()
{
	F2(1, 2, 3);
	F2(1, 2);
	F2(1);
	F2();
    return 0;
}

在上述代码中,我们会发现,有所不同的是在形参的位置上,我们给予了赋值,这样写又什么作用呢?唉~这样写当我们在调用的时候,少给参数的时候,编译器也不会报错,会自动给上默认值.同时也可以多种方式调用函数了.比如在上述代码中:F2(1,2,3)这样的调用时,上面形参的默认值就不会起作用了,而当我们F2(1,2)这样传参的时候,也不会报错,这是编译器会把a和b的值变为,1和2,而c的值就使用我们给的默认参数进行赋值30.同理可以去理解后两个调用,也可以一个都不传哦,这样形参用的全是我们所给的默认值.

1.2 半缺省参数:

半缺省参数是指从右往左连续地为函数的部分参数提供默认值。以下是一个半缺省参数的示例代码:

cpp 复制代码
// 半缺省,从右往左缺省
void F3(int a, int b = 20 , int c = 30)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}

int main()
{
    F3(1);
	F3(1, 2);
	F3(1, 2, 3);
}

在上述代码中,我们只是缺省了b和c,同样可以进行赋值,但是这时我们就不能一个参数也不传了,因为这时我们如果一个都不传的话,这时a是没有默认值的,编译器就会报错,半缺省参数必须从右往左依次提供默认值,不能间隔着给。为什么呢?请看下面的代码:

cpp 复制代码
void F3(int a=10, int b = 20 , int c)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}

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

这时我们这样给默认值的时候,用F3(1,2)进行传参时,2可以赋值给c,但是这个1会赋值给谁呢?是a还是b呢?这是编译器就会存在歧义,就会报错喽,所以,我们平时在写半缺省参数的时候,应该极为注意.细心使用~

1.3 缺省参数的注意事项:

注意:

使用缺省参数可以提高代码的灵活性和可读性,减少函数调用时的参数传递数量。但需要注意以下几点:

  1. 半缺省参数必须 从右往左依次 来给出,不能间隔着给
  2. 缺省参数不能在函数声明和定义中同时出现,如果要给,只能在声明的时候给缺省值
  3. 缺省值必须是常量或者全局变量
  4. C 语言不支持(编译器不支持)
  5. 如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该
    用那个缺省值。
    6.缺省参数在后续的使用中尤为重要,尤其是数据结构中.

2 函数重载:

在C语言中,不支持重名函数存在,这就让一些程序很麻烦,比如要写一个整形数据的交换和浮点数的交换就不行,那么C++祖师爷就改进了这一点---函数重载

在同一作用域内,可以有多个具有相同函数名但参数列表不同(参数的类型、个数或顺序不同)的函数 。这些函数就被称为重载函数

那么具体什么是重载函数呢?请看代码:

cpp 复制代码
namespace bit1
{
	void Swap(int* pa, int* pb)
	{
		cout << "void Swap(int* pa, int* pb)" << endl;
	}
}

namespace bit2
{
	void Swap(int* px, int* py)
	{
		cout << "void Swap(int* pa, int* pb)" << endl;
	}
}

这样的两个函数构不构成重载呢?

显然它们在不同的命名空间中,不符合重载函数中的属于同一作用域这一条件,所以上述两个函数不构成函数重载.让我们来看一下另一个示例:

cpp 复制代码
namespace bit1
{
	void Swap(int* pa, int* pb)
	{
		cout << "void Swap(int* pa, int* pb)" << endl;
	}
}

namespace bit2
{
	void Swap(int* px, int* py)
	{
		cout << "void Swap(int* pa, int* pb)" << endl;
	}
}

using namespace bit1;
using namespace bit2;

如果把它们两个的命名空间展开,是否构成函数重载呢?

显然也是不构成重载的,因为"using namespace"只是把他们展开供全局可以使用这两个命名空间中的成员,并不意味着它们合并同一个作用域,所以还是不符合重载函数中的属于同一作用域这一条件,不构成函数重载,那么怎么才能构成呢?请看正确示例:

2.1 类型一:参数类型不同:

cpp 复制代码
void Swap(int* pa, int* pb)
{
	cout << "void Swap(int* pa, int* pb)" << endl;
}

void Swap(double* pa, double* pb)
{
	cout << "void Swap(double* pa, double* pb)" << endl;
}

上述代码就满足了重载函数的条件:参数类型不同.可以看到上述代码中,形参的类型不同,一个是int,一个是double,当然还可以写其他类型等等等...这样就可以使函数的功能变得更加丰富!

2.2 类型二:参数个数不同:

cpp 复制代码
void f()
{
	cout << "f()" << endl;
}

void f(int a)
{
	cout << "f(int a)" << endl;
}

在上述代码中:显而易见这两个函数的参数个数不同,第一个函数无参,第二个函数有一个整形参数a,可以构成重载函数.

2.3 类型三:参数顺序不同:

cpp 复制代码
void f(int a, char b)
{
     cout << "f(int a,char b)" << endl;
}

void f(char b, int a)
{
     cout << "f(char b, int a)" << endl;
}

大眼一看,好像是一样的,仔细的小伙伴就会发现,上述两个函数的参数的顺序好像不一样,第一个函数是(int a,char b),第二个函数参数是(char b,int a)这样也是可以构成函数重载的哦~

2.4 函数重载的注意事项:

  1. 参数类型必须不同:重载的函数之间参数的类型要有明显区别,不能仅通过可隐式转换的类型差异来重载。

  2. 参数个数不同:这是常见的重载方式之一。

  3. 参数顺序不同:也可以作为重载的依据,但要注意使用时的清晰性。

  4. 不能仅靠返回值不同来重载:因为调用时通常不关心返回值来确定调用哪个重载函数。

  5. 作用域要明确:确保重载的函数都在同一个合理的作用域内,避免混淆。

  6. 避免过度重载:过多的重载可能导致代码复杂难以理解和维护。

  7. 注意歧义:确保参数的组合不会导致调用时产生歧义,编译器能明确地选择正确的重载函数。

  8. 考虑可读性:重载函数的命名和功能设计要符合逻辑,便于其他开发者理解和使用。

3 C++支持函数重载的原理---名字修饰原则:(选看)

为什么C++ 支持函数重载,而 C 语言不支持函数重载呢?
这是因为在 C/C++ 中,一个程序要运行起来,需要经历以下几个阶段: 预处理、编译、汇编、链接

  1. 实际项目通常是由多个头文件和多个源文件构成,而通过 C 语言阶段学习的编译链接,我们
    可以知道,【当前 a.cpp 中调用了 b.cpp 中定义的 Add 函数时】,编译后链接前, a.o 的目标
    文件中没有 Add 的函数地址,因为 Add 是在 b.cpp 中定义的,所以 Add 的地址在 b.o 中。那么
    怎么办呢?
  2. 所以链接阶段就是专门处理这种问题, 链接器看到 a.o 调用 Add ,但是没有 Add 的地址,就
    会到 b.o 的符号表中找 Add 的地址,然后链接到一起 。 ( 老师要带同学们回顾一下 )
  3. 那么链接时,面对 Add 函数,链接接器会使用哪个名字去找呢?这里每个编译器都有自己的
    函数名修饰规则。
  4. 由于 Windows 下 vs 的修饰规则过于复杂,而 Linux 下 g++ 的修饰规则简单易懂,下面我们使
    用了 g++ 演示了这个修饰后的名字。
  5. 通过下面我们可以看出 gcc 的函数修饰后名字不变。而 g++ 的函数修饰后变成【 _Z+ 函数长度
    + 函数名 + 类型首字母】。

    结论: linux 下,采用 gcc 编译完成后,函数名字的修饰没有发生改变。 采用 C++ 编译器编译后结果.

    结论: linux 下,采用 g++ 编译完成后,函数名字的修饰发生改变,编译器将函数参 数类型信息添加到修改后的名字中。
    Windows 下名字修饰规则:

    对比 Linux 会发现, windows vs 编译器对函数名字修饰规则相对复杂难懂 ,但道理都
    是类似的,我们就不做细致的研究了。
  6. 通过这里就理解了 C 语言没办法支持重载,因为同名函数没办法区分。而 C++ 是通过函数修
    饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载
  7. 如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办
    法区分。
    到此有关缺省参数和函数重载的只是就讲解到这啦~希望这篇博客能给您带来一些启发和思考!那我们下次再一起探险喽,欢迎在评论区进行讨论~~~
相关推荐
daily_23335 分钟前
数据结构——小小二叉树第三幕(链式结构的小拓展,二叉树的创建,深入理解二叉树的遍历)超详细!!!
数据结构·c++·算法
laimaxgg29 分钟前
C++特殊类设计(不能被拷贝的类、只能在堆上创建对象的类、不能被继承的类、单例模式)
c++·单例模式
SUN_Gyq38 分钟前
什么是 C++ 中的模板特化和偏特化? 如何进行模板特化和偏特化?
开发语言·c++·算法
愿天垂怜1 小时前
【C++】C++11引入的新特性(1)
java·c语言·数据结构·c++·算法·rust·哈希算法
大帅哥_1 小时前
访问限定符
c语言·c++
小林熬夜学编程1 小时前
【Linux系统编程】第五十弹---构建高效单例模式线程池、详解线程安全与可重入性、解析死锁与避免策略,以及STL与智能指针的线程安全性探究
linux·运维·服务器·c语言·c++·安全·单例模式
凯子坚持 c2 小时前
C++之二叉搜索树:高效与美的极致平衡
开发语言·c++
埋头编程~2 小时前
【C++】踏上C++学习之旅(十):深入“类和对象“世界,掌握编程黄金法则(五)(最终篇,内含初始化列表、静态成员、友元以及内部类等等)
java·c++·学习
亚图跨际2 小时前
MATLAB和C++及Python流式细胞术
c++·python·matlab·流式细胞术
海绵波波1072 小时前
C++11:多线程编程
c++