文章目录
- 前言
- 一.命名空间
- 命名冲突
- 命名空间的使用
- 展开命名空间
- 作用域限定符访问
- 作用域
- 命名空间的合并
- 命名空间的嵌套
- 二.输入输出
- 打印
- 流插入运算符
- 输入
- 流提取运算符
- 三.缺省参数
- 全缺省
- 半缺省
- 跨文件缺省函数参数
- 缺省参数的使用格式
- 四.函数重载
- 参数个数不同
- 参数类型不同
- 参数顺序不同
- 注意事项
- 返回类型不能当做函数重载
- 参数名不能被当做函数重载
- 相同类型的缺省参数不是函数重载
- LInux下的重载函数的符号化
前言
- 因为C++兼容C的大多数语法,所以我们用C逐步讲解,之后再换成C++语法
一.命名空间
- 基本概念 : namespace +空间名+{}
- 切记,最后可没有 ;
命名冲突
#include<stdio.h>
#include<stdlib.h>
int rand = 0;
int main()
{
printf("%d", rand);
return 0;
}
编译时,我们会看到:
-
结论:与头文件stdlib.h的rand函数的命名冲突了
-
问题:不改变此变量的名字的前提下,如何正确打印此变量?
-
答案: 使用命名空间将变量进行封装
注意:命名空间的名字也会发生命名冲突,因此不要起rand!
#include<stdio.h>
#include<stdlib.h>
namespace shunhua
{
int rand = 0;
}
int main()
{
printf("%d", rand);
return 0;
}
- 这样就对了吗?
- 其实不对。
这里其实识别的是rand函数,不是命名空间里的变量!
- 因此:命名空间是将变量或者函数封装,因此是无法直接访问的!
命名空间的使用
展开命名空间
- using namespace + 空间名+ ;
接着上文
#include<stdio.h>
#include<stdlib.h>
namespace shunhua
{
int rand = 0;
}
using namespace shunhua;
int main()
{
printf("%d", rand);
return 0;
}
再次编译,看结果
- 这是怎么回事呢?
- 答案 :使用命名空间,相当于将命名空间暴露与全局范围中,因此又回到了我们要最初要解决的问题。
-
- 命名冲突打印指定变量
- 接着往下讨论
作用域限定符访问
作用域
学过C语言,想必都能看懂这一段代码。
#include<stdio.h>
int rand = 1;
int main()
{
int rand = 0;
printf("%d\n", rand);
return 0;
}
- 全局变量与局部变量同名时,优先使用局部变量。
- 换到C++,域是查找变量的默认优先顺序:
- 1.局部域
- 2.全局域
- 3.命名空间访问变量用——展开的命名空间/限定符访问。
作用域限定符:
: :前面要不加默认使用全局变量,前面加命名空间使用的是命名空间的变量
#include<stdio.h>
namespace shun_hua
{
int rand = 3;
}
int rand = 1;
int main()
{
int rand = 0;
printf("%d\n", rand);//局部变量中查找
printf("%d\n",::rand);//全局域里面查找
printf("%d\n",shun_hua::rand);//在命名空间里面查找
return 0;
}
执行结果:
回头解决要解决的问题:
#include<stdio.h>
#include<stdlib.h>
namespace shunhua
{
int rand = 0;
}
int main()
{
printf("%d", shunhua::rand);
return 0;
}
此时再编译没问题,执行一下:
成功打印出命名空间的值
- 命名冲突打印指定变量
命名空间的合并
- 当我们多次定义同名空间时,相当于定义了一个命名空间,同名的命名会自动将变量与函数进行合并,而不是我们想的重命名!
#include<stdio.h>
#include<stdlib.h>
namespace shun_hua
{
int rand = 0;
int y = 1;
}
namespace shun_hua
{
int x = 2;
int z = 3;
}
int main()
{
printf("%d\n", shun_hua::rand);
printf("%d\n", shun_hua::x);
printf("%d\n", shun_hua::y);
printf("%d\n", shun_hua::z);
return 0;
}
- 这里面的命名空间在合并时,是不允许出现相同的函数或变量出现两次的,如果有会直接在语法上,报错。也就是说命名空间的合并是在编译阶段完成的!
- 如果需要在一个命名空间里面出现两个相同变量怎么办呢?
- 命名空间的嵌套
命名空间的嵌套
- 1.命名空间的嵌套是可以使用相同名字的。
- 2.这样展开命名空间编译器会不知道展开哪个,从而报错
#include<stdio.h>
namespace shun_hua
{
int x = 0;
namespace shun_hua
{
int x = 1;
}
}
//using namespace shun_hua;
int main()
{
printf("%d\n", shun_hua::shun_hua::x);
return 0;
}
输出结果:
二.输入输出
我们首先要明白C++库的基本结构
- 因此C++库是被封装在std命名空间里面的,使用C++库里的对象得通过std访问。
- C++包含头文件是不需要后缀.h
- 输入输出文件在iostream的头文件中
拓展: 早期的头文件在全局域中实现,因此头文件含.h, 后来为了与C头文件区分以及正确的使用命名空间,声明C++的头文件不带.h,因此我们现在看到的iostream不带头文件,而在早期的VC6.0版本还可以使用iostream.h的版本。
打印
流插入运算符
<<跟C语言的左移操作符意义不同,这里是流插入运算符,将数据流入输出流。
打印hello world
#include<iostream>//包含头文件
using namespace std;//使用命名空间,这两者缺一不可
int main()
{
cout << "hello world\n";
return 0;
}
我们一般看到的写法是这样的
#include<iostream>//包含头文件
using namespace std;//使用命名空间,这两者缺一不可
int main()
{
cout << "hello world"<<endl;//这里的endl其实就是"\n"
return 0;
}
- 我们不推荐这样使用命名空间,因为这样将std的命名空间暴露在全局域中, 失去了命名空间创建的意义。
一般推荐这样写
#include<iostream>//包含头文件
using std::cout;
using std::endl;
//这其实是我们使用命名空间的符号的声明
int main()
{
cout << "hello world"<<endl;
std::cout<<"hello world"<<std::endl;
//在使用时也可以这样写,不过在重复多次写这里语句时,比较麻烦。
return 0;
}
输入
流提取运算符
- > >与C语言的右移操作符不同,这里是流提取,将数据输进输入流
输入一个整形,并将这个整形打印
#include<iostream>
using std::cin;
using std::cout;
using std::endl;
int main()
{
int x = 0;
cin >> x;//这是将输入流的数据读取出来放进x
cout << x << endl;
return 0;
}
- 总结:cout和cin会自动识别类型,不像printf和scanf需要手动的控制其类型,因此还是比较方便的。
三.缺省参数
- 缺省参数,是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。
- 也就是说在函数的指定参数有默认值的情况下,调用函数时,指定参数可以传参,也可不传,即为缺省。
全缺省
- 函数的定义或者声明时,函数的参数都有默认值
#include<iostream>
using std::cout;
using std::endl;
int add(int x = 0 ,int y = 0)
{
return x + y;
}
int main()
{
cout << add() << endl;
cout << add(1,2) << endl;
cout << add(1) << endl;
//cout<<add(,2)<<endl;
//语法规定不行
return 0;
}
- 函数缺省,调用时,从左往右进行传参,且从左往右是与函数的参数一 一对应,不能省略的。
半缺省
- 半缺省指的是一部分参数缺省,缺省参数只能从右往左进行缺省
- 注意: 可不是一半的参数缺省
#include<iostream>
using std::cout;
using std::endl;
int add(int x, int y = 0)
{
return x + y;
}
int main()
{
cout << add(1) << endl;
cout << add(1, 2) << endl;
return 0;
}
- 用途:在单链表初始化时,可以指定开辟大小,也可以指定默认参数。
跨文件缺省函数参数
add.h文件
int add(int x = 0,int y = 0);
add.cpp文件
#include"add.h"
int add(int x, int y)
{
return x + y;
}
test.cpp文件
#include"add.h"
#include<iostream>
using std::cout;
using std::endl;
int main()
{
cout<<add(1)<<endl;
cout << add(1,2) << endl;
return 0;
}
- 情况:缺省参数的函数定义与声明在不同文件中
- 在函数声明中需声明缺省参数,**定义则不用写缺省参数,**否则会出现重定义。
- 原因:头文件的函数声明,在编译期间就会被识别,相当于先给编译器一个承诺,函数是什么样子的,而函数定义是在链接期间被检查的,因此声明时前提(承诺),定义是结果(无需写缺省参数)。
缺省参数的使用格式
- 缺省参数只能用常量或者具有全局属性的变量
#include<iostream>
using std::cout;
using std::endl;
#define MAX 100//常量
int x = 0;//全局变量
int add(int max = MAX, int min = x)
{
return max+min;
}
int main()
{
cout << add() << endl;
return 0;
}
四.函数重载
-
重载,顾名思义就是一词多义,比如比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前者是“谁也赢不了(乒乓)!”,后者是“(国足)谁也赢不了!”。
-
函数重载就是函数同名不同义。
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同常用来处理实现功能类似数据类型不同的问题
参数个数不同
#include<iostream>
using std::cout;
using std::endl;
int add()
{
return 0;
}
int add(int x)
{
return x;
}
int main()
{
cout << add() << endl;
cout << add(1) << endl;
}
参数类型不同
#include<iostream>
using std::cout;
using std::endl;
int add(int x ,int y)
{
return 0;
}
int add(double x,double y)
{
return 0;
}
int main()
{
cout << add(1,1) << endl;
cout << add(1.0,1.0) << endl;
}
参数顺序不同
#include<iostream>
using std::cout;
using std::endl;
int add(int x ,double y)
{
return 0;
}
int add(double y,int x)
{
return 0;
}
int main()
{
cout << add(1,1.0) << endl;
cout << add(1.0,1) << endl;
}
注意事项
返回类型不能当做函数重载
- 函数重载是指的函数调用,所发生的类型识别和参数个数,从而确定调用哪个同名函数,而函数的返回值是不能根据函数调用进行确定的
示例:
- 首先说明此代码会报错,原因是在函数调用时,不能区分返回类型,因此调用存在歧义。
#include<iostream>
using std::cout;
using std::endl;
int add(int x ,int y)
{
return 0;
}
float add(int x,int y)
{
return 0;
}
int main()
{
cout << add(1,0) << endl;
cout << add(1,1) << endl;
}
编译结果:
参数名不能被当做函数重载
#include<iostream>
using std::cout;
using std::endl;
int add(int x ,int y)
{
return 0;
}
int add(int y,int x)
{
return 0;
}
编译结果:
相同类型的缺省参数不是函数重载
示例:
#include<iostream>
using std::cout;
using std::endl;
int add(int x ,int y = 0)
{
return 0;
}
float add(int x,int y)
{
return 0;
}
编译结果:
LInux下的重载函数的符号化
Linux下gcc编译的结果
-
结论:函数符号化为函数本身的名字
Linux下g++编译的结果
-
g++编译的生成的函数符号名的规则:_Z+函数名长度+函数名+参数类型的首字母
-
总结:C++本身其实不是通过函数名进行查找,而是通过函数符号化之后的结果,访问地址从而调用函数。