1. 命名空间
在C/C++中,变量、函数和类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。作用域分三种: 全局域, 局部域和文件域 . 使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
//1. 普通的命名空间
namespace N1{// N1为命名空间的名称
// 命名空间中的内容,既可以定义变量,也可以定义函数
int a;
int Add(int left, int right){
return left + right;
}
}
//2. 命名空间可以嵌套
namespace N2{
int a;
int b;
int Add(int left, int right){
return left + right;
}
namespace N3{
int c;
int d;
int Sub(int left, int right){
return left - right;
}
}
}
//3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。所以不要定义重复的变量和函数
namespace N1{
int Mul(int left, int right){
return left * right;
}
}
注意: 一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中
命名空间的成员使用有三种方式:
- 加命名空间名 + 作用域限定符 + 成员
- 使用using将命名空间中成员引入, using + 命名空间 + :: +成员 , 后面使用成员时, 不需要再加作用域了, 相当于暴露在全局当中,如果全局中有同名成员, 会导致命名冲突.
- 使用using namespace + 命名空间名称引入, 将命名空间中所有成员全部暴露在全局域中
最推荐的是第一种方式, 也体现了命名空间的初衷, 就是把命名空间的成员与其他作用域 的成员区分开来, 就算出现同名的成员, 编译器也能唯一确定这个成员是哪个命名空间的. 后面两种方式都会产生命名冲突的问题.
//全局域
//int global = 10;
int fun(){
//局部域
int a = 1;
return a;
}
//文件域
static void fun2(){
}
void testNamespace(){
//全局域: 命名空间可以没有名字
::global = 10;
}
using namespce N; // 使用using namespace 命名空间名称引入
// using N::b; 使用using将命名空间中成员引入
int main()
{
printf("%d\n", N::a); // 加命名空间名称及作用域限定符
printf("%d\n", b);// 直接使用
Add(10, 20);
return 0;
}
2. 缺省参数
2.1 概念
缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。声明或者定义,只能选其一给默认值
void TestFunc(int a = 0){ //默认值
cout<<a<<endl;
}
int main(){
TestFunc(); // 没有传参时,使用参数的默认值0
TestFunc(10); // 传参时,使用指定的实参10
}
2.2 分类
默认值就像备胎,未指定参数时,默认值就上;指定参数时, 默认值就退下
- 全缺省参数(参数全部都有默认值)
void TestFunc(int a = 10, int b = 20, int c = 30){
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
- 半缺省参数(部分参数没有设置默认值)
void TestFunc(int a, int b = 10, int c = 20){
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
注意:
- 半缺省参数必须从右往左依次来给出,不能间隔着给
- 缺省参数不能在函数声明和定义中同时出现, 如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用哪个缺省值
- 缺省值必须是常量或者全局变量
- C语言不支持(编译器不支持)
3. 函数重载
函数重载是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题 ----> 同一个系列的不同版本的手机
- 前提: 多个函数在同一个作用域下(同一个系列的不同版本的手机, 都是iphone)
- 函数名相同 可以参数信息(个数, 参数类型, 参数顺序)不同,
int Add(int left, int right){
return left+right;
}
double Add(double left, double right){
return left+right;
}
long Add(long left, long right){
return left+right;
}
int main(){
Add(10, 20);
Add(10.0, 20.0);
Add(10L, 20L);
return 0;
}
C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区
分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
C编译器: int sub(int,int) 底层的函数名为_sub
C++编译器: int sub(int, int) 底层的函数名为...(不认识)
g++编译器: int sub(int, int) 底层的函数名为 _Z3subii, 3为函数名长度, 后面的ii为参数类型(i,f,c)
C编译器函数名的修饰规则: 只包含函数名
C++编译器函数名的修饰规则为: 函数名+参数信息(顺序, 个数, 类型)
导致C++支持函数重载, C不支持
链接:
文件之间的交互, 汇编阶段产生导出符号表和未解决符号表(函数名和起始的位置) 链接的时候去其他文件中找
// 预处理: 头文件展开, 去注释, 条件编译, 宏替换
// 编译: 语法检查, 把源代码编译成汇编代码
// 汇编: 把汇编代码编译成机器码 --> 目标文件(.o, .obj), --> 导出符号表 ,未解决符号表
// 链接: 把所有的机器码转换成一个可执行文件, --> 找到所有指令的地址
a.cpp
int funa(int a, int b){}
生成.obj文件, 还有导出符号表: funa, 0X45(地址)
未解决符号表: 空
#include "a.h"
b.cpp
int funb(int a){}
int main(){
funa(1, 2);
funb(3);
}
生成b.obj
导出符号表: funb 0X63(地址)
未解决符号表: funa?(去找funa的地址, 从而找到funa的所有
有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern “C”,意思是告诉编译器,将该函数按照C语言规则来编译。