目录
1. C++关键字(C++98)
2. 命名空间
2.1 命名空间定义
2.2 命名空间使用
3.C++输入输出
4.缺省函数
缺省参数分类
5. 函数重载
5.1 函数重载概念
5.2 C++支持函数重载的原理
5.3 extern “C”
C语言和C++源文件后缀是不同的
C++源文件的后缀是 .cpp。后缀不同工程不同,背后的使用的编译器是不一样的。
#include
#include
int rand = 10;int main()
{printf("%d\n", rand);return 0;
}
C语言没办法解决类似这样的命名冲突问题,所以C++提出了namespace来解决
编译后后报错:error C2365: “rand”: 重定义;以前的定义是“函数”
#include//之前在全局作用域中可以定义的内容,在命名空间内都可以定义
namespace zx
{//定义变量int a = 10;int b = 20;//定义函数int Add(int x, int y){return x + y;}//定义结构体struct BTNode{struct BTNode* left;struct BTNode* right;int data;};
}int a = 10;
int b = 20;
int Add(int x, int y)
{return x + y;
}
int main()
{return 0;
} 2.命名空间嵌套:一个命名空间中,可以包含其他的命名空间
类似于:学校可以看作一个命名空间,学校下包含各个学院,学院下又包含不同专业
namespace zx
{int a = 120;int b = 20;int Add(int x, int y){return x + y;}namespace zx2{int x = 10;int y = 20;}
} 3.同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
ps:一个工程中的test.h和上面test.cpp中两个N1会被合并成一个int a = 10;int main()
{int a = 30;printf("%d\n", a);return 0;
} 如上,直接打印出来a为30,要打印全局作用域中的a,就要使用作用域运算符
::作用域运算符
printf("%d\n", ::a); 这样编译器就会找全局作用域中的a ,找到了就打印,找不到会报错
以此类推,你要找上面 定义的 命名空间中的 a,加上名字即可printf("%d\n", zx::a); 这样打出来就是120
2.使用using将命名空间中某个成员引入 使用using,相当于把b变成了当前文件中的全局变量,下面打印出来的结果相同的,但是如果你在文件全局作用域中再次定义一个b。就会报错using N::b;
int main()
{printf("%d\n", N::a);printf("%d\n", b);return 0;
} 3.使用using namespace 命名空间名称 引入
把命名空间N中所有成员都定义为当前文件中的全局变量;
但是一般不推荐使用,容易冲突
using namespce N;
int main()
{printf("%d\n", N::a);printf("%d\n", b);Add(10, 20);return 0;
} 命名空间嵌套时,内部命名空间中成员的使用方式
如下,打印出来结果为x的值
namespace zx
{int a = 110;int b = 20;namespace zx2{int x = 10;int y = 20;}
}
int main()
{printf("%d\n", zx::zx2::x);return 0;
} cin输入,cout输出
cin在接受输入时,不能接受空格 tab 回车,如要接受,使用getchar
1. 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件以及按命名空间使用方法使用std。 2. cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含< iostream >头文件中。 3. <<是流插入运算符,>>是流提取运算符。 4. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型。 5. 实际上cout和cin分别是ostream和istream类型的对象,>>和<<也涉及运算符重载等知识,这些知识我们我们后续才会学习,所以我们这里只是简单学习他们的使用。后面我们还有有一个章节更深入的学习IO流用法及原理。 写项目时推荐如下:#include
int main()
{std::cout << "12" << std::endl;return 0;
} 练习时可使用:
#include
using namespace std;
int main()
{cout << "12" << endl;return 0;
} std是C++标准库的命名空间,如何展开std使用更合理呢? 1. 在日常练习中,建议直接using namespace std即可,这样就很方便。 2. using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模大,就很容易出现。所以建议在项目开发中使用,像std::cout这样使用时指定命名空间 + using std::cout展开常用的库对象/类型等方式 void Func(int a = 0)
{cout< #include
using namespace std;
void fun(int a = 1, int b = 2, int c = 3)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl;cout << "------" << endl;
}
int main()
{fun();fun(10);fun(10, 20);fun(10, 20, 30);return 0;
} 
大家对比结果可看出规律,当传入不全时,依次传参
2.半缺省参数 部分参数带有默认值 a和b有默认值,c没有--》编译报错 a和c有默认值,b没有--》编译报错 b和c有默认值,a没有--》编译成功 注意事项: 1. 半缺省参数必须从右往左依次来给出,不能间隔着给 2. 缺省参数不能在函数声明和定义中同时出现 假设声明和定义的位置可以同时给出,如果两个地方的默认值不一样,编译器就不能判断使用那个值。 一般情况下,如果缺省值放在函数定义的位置,用户就不知道函数的形参是否带默认值。所以一般放在函数声明的地方。 3. 缺省值必须是常量或者全局变量 4. C语言不支持(编译器不支持)int Add(int left, int right)
{return left + right;
}
double Add(double left, double right)
{return left + right;
}int main()
{cout << Add(1, 2) << endl;cout << Add(1.2, 2.3) << endl;return 0;
} cout << Add(1, 2.3) << endl; 此时,如果没有参数类型对应的函数,编译器会对实参进行隐式类型转换,如果转换之后有类型匹配的函数则调用,否则报错,如下,会把double型转换为int型,输出为都是3 int Add(int left, int right)
{return left + right;
}
int main()
{cout << Add(1, 2) << endl;cout << Add(1.2, 2.3) << endl;return 0;
} 一些注意的点: 1. 实际项目通常是由多个头文件和多个源文件构成,而通过C语言阶段学习的编译链接,我们可以知道, 【当前a.cpp中调用了b.cpp中定义的Add函数时】,编译后链接前,a.o的目标文件中没有Add的函数地 址,因为Add是在b.cpp中定义的 c语言编译器仅仅在函数名字前添加了下划线
所以,下面两个函数在c语言中的名字最终被修饰为: _Add
虽然两个方法的原型不同,编译器认为这两函数为同一函数,导致重定义出错。
int Add(int left, int right)
{return left + right;
}
double Add(int left, int right)
{return left + right;
} 上面同样的代码,在C++中运行无报错 2. 所以链接阶段就是专门处理这种问题,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符号表中找Add的地址,然后链接到一起。(老师要带同学们回顾一下) 3. 链接时,面对Add函数,这里每个编译器都有自己的函数名修饰规则。 4. 由于Windows下vs的修饰规则过于复杂,而Linux下g++的修饰规则简单易懂,下面我们使用了g++演示了这个修饰后的名字。 5. 通过下面我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母】。 6. C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
7. 如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分