目录
1. C++关键字(C++98)
在讨论有多少关键字的时候,要说清楚在那个版本之下,在C++98中,C++
总计
63
个关键字,
C
语言
32
个关键字,这里只是一个展示表,在后面的学习中慢慢掌握
![](https://i-blog.csdnimg.cn/blog_migrate/5e4fde9d53c40fc11f573189cb2aa43f.png)
C语言和C++源文件后缀是不同的
C++源文件的后缀是 .cpp。后缀不同工程不同,背后的使用的编译器是不一样的。
2. 命名空间
在
C/C++
中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。
使用命名空间的目的是对标识符的名称进行本地化
,以
避免命名冲突或名字
污染
,
namespace
关键字的出现就是针对这种问题的
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{
printf("%d\n", rand);
return 0;
}
C语言没办法解决类似这样的命名冲突问题,所以C++提出了namespace来解决
编译后后报错:
error C2365: “rand”:
重定义;以前的定义是
“
函数
”
2.1 命名空间定义
定义命名空间,需要使用到
namespace
关键字
,后面跟
命名空间的名字
,然
后接一对
{}
即可,
{}
中即为命名空间的成员。
下面是最基本的命名空间定义
#include<iostream>
//之前在全局作用域中可以定义的内容,在命名空间内都可以定义
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
会被合并成一个
2.2 命名空间使用
1.加命名空间名称及作用域限定符
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;
}
3.C++输入输出
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 <iostream>
int main()
{
std::cout << "12" << std::endl;
return 0;
}
练习时可使用:
#include <iostream>
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
展开常用的库对象
/
类型等方式
4.缺省函数
声明或定义函数时,可以给函数的参数带上默认值,在调用函数时,如果用户没有指定实参,则使用默认值,如果用户指定了实参,则使用用户传递的实参
void Func(int a = 0)
{
cout<<a<<endl;
}
int main()
{
Func(); // 没有传参时,使用参数的默认值
Func(10); // 传参时,使用指定的实参
return 0;
}
缺省参数分类
1.全缺省参数:所有的参数都有默认值
#include <iostream>
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语言不支持(编译器不支持)
5. 函数重载
5.1 函数重载概念
函数重载:
是函数的一种特殊情况,
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;
}
5.2 C++支持函数重载的原理
在
C/C++
中,一个程序要运行起来,需要经历以下几个阶段:
预处理、编译、汇编、链接
在上面程序中,编译阶段对形参类型进行推演:编译器确认实参的类型
上面首先 1,2都是int,两个形参都是int,最终就会调用 int 的ADD函数
后面double类型的同理
如果如此输入
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语言编译器仅仅在函数名字前添加了下划线
![](https://i-blog.csdnimg.cn/blog_migrate/0391d45b62573ee31c653a2a16d710c7.png)
所以,下面两个函数在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++
是通过函数修饰规则来区
分,只要参数不同,修饰出来的名字就不一样,就支持了重载
。
![](https://i-blog.csdnimg.cn/blog_migrate/e019c0ad1257dd55bb09024379d739be.png)
7.
如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分
5.3 extern “C”
由于
C
和
C++
编译器对函数名字修饰规则的不同,在有些场景下可能就会出问题,比如:
1. C++
中调用
C
语言实现的静态库或者动态库,反之亦然
2.
多人协同开发时,有些人擅长用
C
语言,有些人擅长用
C++
在这种混合模式下开发,由于
C
和
C++
编译器对函数名字修饰规则不同,可能就会导致链接失败,在该种场景下,就需要使用extern "C"
。
在函数前加
extern "C"
,意思是告诉编译器,将该函数按照
C
语言规则来编译
具体创建规则可以在网上查找