【C++】命名空间、缺省参数和函数重载的使用

1、命名空间

1.1 为什么有命名空间

  C++作为C语言的扩展,在C语言中,由于本身自带大量变量和函数名,在使用者进行命名的时候,为了避免出现命名冲突或命名污染,C++的开发者们创造了命名空间。

比如:
很明显命名冲突了


#include <stdio.h>
#include <math.h>
int abs = 1;

int main()
{
	printf("%d\n", abs);

	//编译报错:error c2365 "abs":重定义;以前的定义是"函数"
	return 0;
}

如果我们一定要在引用math.h下定义abs为一个变量,并且使用它,C语言是实现不了的,这个时候就需要C++的命名空间。

1.2 定义命名空间

在C++中,有一个域作用限定符 : : ,它的作用是引用限定的域,默认情况下访问全局域。
比如:

#include <stdio.h>

int a = 0;

int main()
{
	int a = 1;

	printf("%d\n", a); //1

	// :: 域作用限定符
	// 默认访问全局
	printf("%d\n", ::a); //0

	return 0;
}

定义命名必须使用关键词 namespace,后面跟空间名,这个名可以自己取,之后再接{}。

#include <stdio.h>
#include <math.h>

//不影响 变量生命周期,只是改变限定域和编译查找规则
//定义在静态区
namespace N1
{
	int abs = 10;
	int x = 1;
}

void func()
{
	//先在局部找,没有就从全局找
	printf("%p\n", abs); //打印abs函数地址  地址随机
	printf("%d\n", N1::abs); //通过域操作符打印N1命名空间中的abs。  10
	printf("%d\n", N1::x);   //通过域操作符打印N1命名空间中的x。  1
}

命名空间还可以嵌套使用,并且命名空间除了定义变量,还可以定义类型和函数

#include <stdio.h>
namespace N1
{
	int abs = 10;
	int x = 1;

	int Add(int left, int right)
	{
		return left + right;
	}

	struct Node
	{
		struct Node* next;
		int val;
	};

	namespace N2
	{
		int a = 1;
		
		struct Node
		{
			struct Node* next;
			int val;
		};
		
	}

}

int main()
{
	printf("%d\n", N1::Add(1, 2));
	struct N1::Node node1;

	// struct N2::Node node2; //err 由于 N2 在 N1 里 需要先调用N1

	struct N1::N2::Node node3;

	N1::x = 5;
	N1::N2::a = 2;
	return 0;
}


同一个项目中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。

这也说明了如果在同一个文件如果有两个相同名称的命名空间,也会合并。
因为本质上,在预处理的时候,会将引用的文件展开,放在一个文件里,所以本质都是放在一个文件。

1.3使用命名空间

加命名空间名称以及作用域限定符

int main()
{
    printf("%d\n", N::a);
    return 0;    
}


使用using将命名空间中某个成员引入

using N::b;
int main()
{
    printf("%d\n", N::a);
    printf("%d\n", b);
    return 0;    
}

使用using namespace 命名空间名称 引入

using namespce N;
int main()
{
    printf("%d\n", N::a);
    printf("%d\n", b);
    Add(10, 20);
    return 0;    
}

在这里介绍一下C++ 的标准命名空间 std,并通过std完成三种hello world的写法。

第一种
  using 将命名空间std展开,任意std里定义的都可以直接使用,这种叫做全部展开,但这有一个坏处,就是在复杂的std命名空间中,与我们自己定义的名称避免不了冲突,所以在大型项目避免全部展开std。

//1.
#include <iostream>

using namespace std;

int main()
{
	cout << "Hello world!!" << endl;
	return 0;
}

第二种
  部分展开,指定std命名空间内展开,这是比较好的,因为能很好避免命名冲突。

//2.
#include <iostream>
using std::cout;
using std::endl;

int main()
{
	cout << "Hello world!!" << endl;
	return 0;
}

第三种
这种很规矩,不过第二种方法可以更加 便利。

//3.
#include <iostream>
int main()
{
	std::cout << "Hello world!!" << std::endl;
	return 0;
}

2、缺省参数

2.1 什么是缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个缺省值,在调用该函数时,如果没有指定实参,则采用该函数形参的缺省值,否则使用指定的实参。

void Func(int a = 0)
{
 cout<<a<<endl;
}
int main()
{
 Func();     // 没有传参时,使用参数的默认值
 Func(10);   // 传参时,使用指定的实参
return 0;
}

2.2 缺省参数分类

全缺省

void Func(int a = 10, int b = 20, int c = 30)
 {
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }

半缺省

出现形参和缺省值

void Func(int a, int b = 10, int c = 20)
 {
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }

值得注意的是:

  1. 缺省参数只能从右往左连续给,不能间隔给。
  2. 函数定义和函数声明中不能同时出现缺省参数,缺省参数一般都设在声明中。(因为如果同时给,值又不同,编译器无法识别) 。
  3. 缺省参数只能是常量或全局变量。

2.3 缺省参数的应用场景

看代码:

//缺省参数的用法
namespace N1
{
	//缺省参数不能在函数声明和定义中同时出现
	//放在声明中
	typedef struct stack
	{
		int top;
		int* a;
		int capacity;
	}ST;


	void StackInit(ST* st, int size)
	{
		st->a = (int*)malloc(sizeof(int) * size);
		st->capacity = 0;
		st->top = 0;
	}
}

namespace N1
{
	void StackInit(ST* st, int size = 4);
}


//缺省能让函数在多种场景下操作
//不能直接修改函数定义
int main()
{
	//未知大小时,使用默认size = 4.
	N1::ST st1;
	StackInit(&st1);

	//已知大小 只需在定义的时候改就行,如果是直接定义变量,在未知大小就无法传值了。
	N1::ST st2;
	StackInit(&st2, 100);

	return 0;
}

3、函数重载与缺省参数

3.1 什么才是函数重载

函数重载的前提,一定是函数在同一作用域下才会有重载。

第一种:
函数重载 函数名相同,参数不同(类型)

//函数重载  函数名相同,参数不同(个数、类型、顺序)

int add(int x, int y)
{
	return x + y;
}

double add(double x, double y)
{
	return x + y;
}

void swap(int* px, int* py)
{
	int tmp = *px;
	*px = *py;
	*py = tmp;
}

void swap(double* px, double* py)
{
	double tmp = *px;
	*px = *py;
	*py = tmp;
}

int main()
{
	cout << add(1, 2) << endl;
	cout << add(1.1, 2.2) << endl;

	int a = 0, b = 1;
	swap(&a, &b);

	double c = 1.1, d = 2.2;
	swap(&c, &d);

	cout << a << endl;
	cout << c << endl;

	return 0;
}

第二种:
函数重载 函数名相同,参数不同(顺序)
顺序不同是形参类型顺序不同

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

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

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

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

第三种:
函数重载 函数名相同,参数不同(个数)

//参数个数不同
void f()
{
	cout << "f()" << endl;
}

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

int main()
{
	f();
	f(1);
	f(0, 'A');
	f('A', 1);

	return 0;
}

3.2 构成函数重载,但编译不能通过

拥有缺省参数的f()和无参数的f(),拥有函数重载参数个数不同的要求,但是 f() 在调用时编译器不能识别,所以编译不能通过,也就报错了。

//构成函数重载 -- 但f()调用会报错,存在歧义
void f()
{
	cout << " f() " << endl;
}

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

int main()
{
	f(10);//可以编译
	f(10, 20); //可以编译


	/*f();*/ //err 歧义 二义性
	return 0;
}

3.3 函数重载的原理–命名修饰

一个程序的运行,需要通过预处理、编译、汇编和链接,在汇编中会有一个符号表的形成,在C语言中,符号表主要包括函数名和函数地址,而在C++中,符号表通过对函数名进行了修改,加上了函数的参数类型,这就使得C++出现了函数重载。

如以下一串代码

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

void func(int a, double b, int* p)
{}

int main()
{
	Add(1, 2);
	func(1, 2, 0);

	return 0;
}

C程序正常都是单单函数名加地址,
而C++程序 在Linux环境下通过反汇编看到,函数Add和func的函数名都进行了改变_Z3代表长度,Addii分别代码函数名和参数类型。
在这里插入图片描述
所以为什么函数重载有参数类型、参数类型顺序和参数类型个数这几种情况。

下面讨论一个问题:
函数返回类型不同是否能作为函数重载条件?

如果按上述所说,我们将函数返回值也加入命名不就行了。

答案是不能,为什么呢?

看代码
如果这种方式能构成重载,那么在函数调用的时候,并没有什么能够显示返回值的类型,编译器无法区分,所以返回值不用加入命名规则,所以也就不构成重载。

int func(int a, double b, int* p)
{
	return a;
}

double func(int a, double b, int* p)
{
	return b;
}

int main()
{
	func(1, 2, 0);
	func(1, 2, 0);

	return 0;
}

本章完~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值