C++入门:命名空间,缺省参数,函数重载详解

目录

1. C++关键字(C++98)

命名冲突:

2. 命名空间:namespace关键字

2.1 命名空间定义

普通命名空间例子:

1.域作用限定符 ::

2.如何访问命名空间域内的变量:

3.不同命名空间中的同名变量如何使用:

4.命名空间的性质

细节1:这里n1是结构体,结构体已经升级成类,就可以不加struct

细节2:命名空间不影响声明周期,只是隔离了名字冲突,a还是全局变量

细节3:命名空间不能放在main函数中,只能放在全局

细节4:命名空间一般是放在头文件中

2.2 命名空间使用

1.using namespace byte ;

例子1:

例子2:using namespace byte: :cache

例子3:

例子4:

2.using namespace std;

3. C++输入&输出

(1.cin和cout讲解:

(2. C++输入输出更方便,

(3.输出支持空格 冒号分割

(4.想保留小数位数还是用c语言吧,他们输入输出可以混着用,c++也能控制但是很复杂,不建议学

4. 缺省参数/默认参数

4.1 缺省参数概念

4.2 缺省参数分类

(1.多个缺省参数:

(2.缺省参数的意义:

(3.总结:

5. 函数重载

5.1 函数重载概念+详解

为什么C语言不支持重载,C+ +支持重载?详解:


1. C++关键字(C++98)

C++总计63个关键字,C语言32个关键字

ps:下面我们只是看一下C++有多少关键字,不对关键字进行具体的讲解。后面我们学到以后再细讲。

C++的标准输入输出流库#includ<iostream>:相当于C语言里面的<stdio.h> ,该头文件定义了 cin、cout,endl等等。

using namespace std; 又是什么东西呢,接下来由此引入命名冲突和命名空间关键字 namespace

他是放开了C++库std

命名冲突:

命名冲突:同一个作用域不能定义同名变量 -- C语言没有很好的解决这个问题,CPP引入namespace解决这个问题

int rand = 0; int main() { printf("%d\n", rand); return 0; } 此时没问题,但是包含某些库的时候就会报错: #include<stdlib.h> //命名冲突 int rand = 0; //命名冲突 int main() { printf("%d\n", rand); return 0; }

命名冲突 -- C语言没有很好的解决这个问题,CPP引入namespace解决这个问题

2. 命名空间:namespace关键字

在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作

用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字

污染,namespace关键字的出现就是针对这种问题的

2.1 命名空间定义

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名

空间的成员。

普通命名空间例子:

(1)

此处打印的是函数地址,是stdlib.h中的一个创造随机值函数rand的地址

因为命名空间关键字namespace把变量rand=0关进了名叫bit的命名空间域中,隔离起来

(2)

#include<iostream>

void f()
{}
int f = 0;
int main()
{
	printf("%p\n", f);
}
————————这样写就不对,改成下面这样就对了:
#include<iostream>

namespace bit // 命名空间域
{
	int f = 0;
}
void f()
{}
int main()
{
	printf("%p\n", f);
}

同名变量在不同域可以存在:

局部有限,则打印1,如果找不到局部变量才打印0

注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中

1.域作用限定符 ::

(1)情景1 ::a 左边域是空白(左边有没有空格无所谓),默认访问全局域的a

(2)情景2 假如一个命名空间叫bit,在其中定义了变量f和rand ,想用 f 或 rand ,就要展开bit写成 bit::f 和 bit::rand

(3)情景3 假如一个命名空间叫zsh,在其中定义了一个结构体类型 ListNode ,用这个类型定义变量

namespace zsh { int a = 0; void f() { printf("void f()\n"); } struct ListNode { int val; struct ListNode* next; }; } 可以这样定义变量: zsh::ListNode* n1=NULL ; //结构体指针 struct zsh::ListNode* n1 = NULL; zsh::ListNode n2 ; //结构体变量 struct zsh::ListNode n2;

2.如何访问命名空间域内的变量:

3.不同命名空间中的同名变量如何使用:

#include <stdio.h> #include <stdlib.h> namespace zsh { int a = 0; void f() { printf("void f()\n"); } struct ListNode { int val; struct ListNode* next; }; } namespace bit { struct ListNode { int val; struct ListNode* next; }; struct QueueNode { int val; struct QueueNode* next; }; } int main() { zsh::ListNode* n1 = NULL; bit::ListNode* n2 = NULL; return 0; }

4.命名空间的性质

1、同名的命名空间是可以同时存在的,编译器编译时会合并

2、命名空间可以嵌套

3. 命名空间中的内容,既可以定义变量,也可以定义函数,可以定义类型(结构体类型)

(这里的n1是结构体,再使用时不用带前面的类型)

细节1:这里n1是结构体,结构体已经升级成类,就可以不加struct

(左边是c++写法,右边是严格c语言写法)

细节2:命名空间不影响声明周期,只是隔离了名字冲突,a还是全局变量

细节3:命名空间不能放在main函数中,只能放在全局

细节4:命名空间一般是放在头文件中

2.2 命名空间使用

命名空间中成员该如何使用呢?比如:

命名空间的使用有三种方式:

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

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

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

1.using namespace byte ;

//这里的意思是把byte这个命名空间定义的东西放出来,使namespace byte这个命名空间无效

例子1:

前面已经有同名命名空间:

namespace byte
{
	int b = 0;

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

namespace byte
{
	// a还是全局变量,命名空间不影响生命周期
	int a = 0;

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

如果先展开data再展开byte就错了

例子2:using namespace byte: :cache

只是展开了byte里的cache,那么可以把byte: :cache省了,原本是byte: :cache::Node n1 ,现在写成 struct Node n1 ;

但是不能只省byte ,写成struct cache: :Node n2; 就错了

例子3:

如果都放出来,data和cache中都有struct Node 这个结构体,就又重名了,就错了

最规范的还是展开一个byte

例子4:

直接using namespace bit; 会全部放出来,虽然能用f了,但是也把rand放出来了,变量rand和stdlib.h 中的函数rand又同名了,就错了

所以要用什么放什么:

单独放不用加namespace,这是语法规定

2.using namespace std;

std是c++标准库的命名空间,#include 是c++头文件

using namespace std;就是展开c++头文件的内容

如果不写这句就需要指定命名空间std里的内容 std::cout

如果下面这样放开了std,再定义cout就和库里面的某些函数冲突了,一用cout就会报错

干脆不放开,然后指定使用命名空间std里面的内容,比如std::cout或者std::endl就可以使用

只放cout,指定不指定都可以

不能连着放

3. C++输入&输出

新生婴儿会以自己独特的方式向这个崭新的世界打招呼,C++刚出来后,也算是一个新事物,

那C++是否也应该向这个美好的世界来声问候呢?我们来看下C++是如何来实现问候的。

说明:

1. 使用cout标准输出(控制台)和cin标准输入(键盘)时,必须包含< iostream >头文件以及std标准命名空间。

注意:早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应头文件

即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,规定C++头文

件不带.h;旧编译器(vc 6.0)中还支持格式,后续编译器已不支持,因此推荐使用

+std的方式。

(1.cin和cout讲解:

cin和cout看成控制台,>>流提取运算符和

cin>>a ,箭头方向由cin指向a,相当于cin内数据流入a

cout

endl相当于换行

(2. C++输入输出更方便,

不需增加数据格式控制,比如:整形--%d,字符--%c

一行输入多个,一行输出多个,且不用定义格式,自动控制输出

endl相当于换行,这里用endl和\n是一样的

(3.输出支持空格 冒号分割

(4.想保留小数位数还是用c语言吧,他们输入输出可以混着用,c++也能控制但是很复杂,不建议学

(5.scanf和printf比cin和cout快一些,大量输出时有可能用,平时不考虑

4. 缺省参数/默认参数

大家知道什么是备胎吗?

C++中函数的参数也可以配备胎。

4.1 缺省参数概念

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

这里的int a=0 就是缺省参数

4.2 缺省参数分类

(1.多个缺省参数:

全缺省:缺省参数给全了

多个缺省参数,只能传完第一个再传第二个,不能直接传第二个

半缺省:缺省参数给了一部分,即缺省部分

①最少传一个(第一个),不能一个不传

②半缺省 必须从右往左缺省,并且是连续的

必须从右往左缺省,并且是连续的例子:

(2.缺省参数的意义:

struct Stack
{
	int* a;
	int size;
	int capacity;
};

void StackInit(struct Stack* ps, int n = 4)
{
	assert(ps);
	ps->a = (int*)malloc(sizeof(int)*n);
	ps->size = 0;
	ps->capacity = n;
}

int main()
{
	TestFunc(1);
	TestFunc(1, 2);
	TestFunc(1, 2, 3);

	Stack st;
	//StackInit(&st);    //不给参数就是缺省参数,n=4
	StackInit(&st, 100); //也可以给参数,n=100

	return 0;
}

(3.总结:

1. 半缺省参数必须从右往左依次来给出,不能间隔着给

2. 缺省参数不能在函数声明和定义中同时出现(函数声明和函数定义分离的情况)

(解释:怕你用不同的缺省参数

那该怎么给?缺省参数和声明在编译阶段处理!

声明定义都给缺省参数不行,存在歧义

声明不给定义给缺省参数不行,如果只给定义里面了,头文件#include "Queue.h" 在声明里面找不到缺省参数就报错,函数定义是在链接时连接起来的

只能声明给定义不给缺省参数,因为头文件#include "Queue.h" 展开,.cpp向上找会找到声明里面的缺省参数,)

3. 缺省值必须是常量或者全局变量

4. C语言不支持(编译器不支持)

5. 函数重载

自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。

比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前

者是“谁也赢不了!”,后者是“谁也赢不了!”

5.1 函数重载概念+详解

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

例题:以下不是double compare(int,int)的重载函数的是( )

A.int compare(double,double)

B.double compare(double,double)

C.double compare(double,int)

D.int compare(int,int)

只要参数列表不一样,就构成函数重载,ABC都对,D函数重载不能依靠返回值的不同来构成重载,因为调用时无法根据参数列表确定调用哪个重载函数,故错

正确例子:

错误例子:

正确:

正确:
// 顺序不同
void f(int i, double d)
{
cout << "void f(int i, double d)" << endl;
}

void f(double i, int d)
{
cout << "void f(double i, int d)" << endl;
}
int main()
{
	f(1, 22.2);	
        f(22.2, 1);
}

意义:使用相同功能的函数时,如果类型不同可以使用函数重载

void swap(int* p1, int* p2)
{
	int x = *p1;
	*p1 = *p2;
	*p2 = x;
}

void swap(double* p1, double* p2)
{
	double x = *p1;
	*p1 = *p2;
	*p2 = x;
}

int main()
{
	int a = 0, b = 1;
	double c = 1.1, d = 2.2;
	swap(&a, &b);
	swap(&c, &d);

	return 0;
}

为什么C语言不支持重载,C+ +支持重载?详解:

 举例说明:有三个文件头文件 f.h 和 c++文件 f.cpp , test.cpp

//    f.h

#include<stdio.h>
void f(int a,double d);
void f(double d,int a);

//    f.cpp

#include"f.h"
void f(int a, double b)
{
    printf("%d, %f\n", a, b);
}
void f(double b,int a)
{
    printf("%f, %d\n",b,a) ;
}

//    test.cpp

#include"f.h"
int main( )
{
    f(1,2.222) ;
    f(2.222, 1) ;
    return 0;
}

编译链接过程:                             

1.预处理:预处理会把头文件展开到cpp文件中,f.h就没了,同时宏替换,条件编译,去注释,f.cpp 和test.cpp变为f.i 和 test.i 文件                                                                                               

2.编译:检查语法,生成汇编代码,f.i 和 test.i 文件 转化成 f.s 和 test.s文件                                   

3.汇编:把汇编代码转换成二进制的机器码,f.s 和 test.s文件 转化成 f.o 和 test.o文件                   

4.链接:找调用函数的地址,链接对应上,合并到一起,把f.o 和 test.o合并成a.out。

汇编后:.o文件中有 函数调用指令 和 符号表 

符号表:记录了函数定义和函数名的和函数地址映射关系 还有其他的东西暂时先不用管 

main函数调用指令中去调用两个 f函数 时,会有一句指令叫call 函数名(函数地址),还未链接时,test.cpp中没有函数定义,call指令只需要找函数声明,声明是一种承诺,承诺函数定义在别的文件中,有了"承诺"后call指令可以先不知道函数地址,随后进行链接,链接是把 f.o 和 test.o 里面所有的指令链接起来成为一个文件叫 a.out ,此时兑现"承诺",call指令通过函数的名字去上面的符号表里面找到函数地址把“?未知地址”添上函数地址才能成功执行call指令(符号表汇总了函数名的和函数地址映射关系),但是:c语言中call后面的函数名就是原来的函数名,没有修改,没有区别,如果函数名相同,再去找函数地址就会出问题,因为无法区分两个同名函数。c++正是对函数名做了修饰进行区分才能够进行函数重载:

 函数名不再是原本的函数名了,而是:_Z  函数名长度 函数名 类型首字母  的形式进行记录函数,c++把参数类型首字符带进命名规则中去了,因此去找函数地址就可以区分同名函数,这也说明了为什么函数重载需要 形参个数 或 类型 或 顺序 不同了。

  • 15
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值