C++入门第一讲:
文章目录
1.C++关键字(C++98)
C++总计63个关键字,C语言32个关键字
2.命名空间
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是
对标识符的名称进行本地化
,以
避免命名冲突或名字污染
,namespace关键字的出现就是针对这种问题的。
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
// C语言没办法解决类似这样的命名冲突问题,所以C++提出了namespace来解决
int main()
{
printf("%d\n", rand);
return 0;
}
// 编译后后报错:error C2365: “rand”: 重定义;以前的定义是“函数”
2.1命名空间的定义
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。
#include<stdio.h>
#include <stdlib.h>
// 域
// 局部域/全局域:1、使用 2、生命周期
//int a = 2;
void f1()
{
int a = 0;
printf("%d\n", a);//局部优先原则,此处的局部变量a
printf("%d\n",::a); // ::域作用限定符,此处是全局变量a
}
void f2()
{
int a = 1;
}
int main()
{
printf("%d\n", a);
f1();
return 0;
}
#include "List.h"
#include "Queue.h"
//int main()
//{
// struct AQueue::Node node1; //这里我们使用的是Queue.h文件中定义的命名空间AQueue里面的Node
// struct BList::Node node2; //这里我们使用的是List.h文件中定义的命名空间BList里面的Node
//
// AQueue::min++;
// BList::min++;
//
//
//
// return 0;
//}
namespace lyb
{
namespace A //命名空间的嵌套
{
struct Node
{
struct Node* next;
int val;
};
struct Queue
{
struct Node* head;
struct Node* tail;
};
int min = 0;
}
}
int main()
{
struct lyb::A::Node node1;
struct lyb::B::Node node2;
lyb::A::min++;
lyb::B::min++;
return 0;
}
命名空间是影响使用不影响生命周期,我们有三种访问命名空间的方式 :
1.指定命名空间访问
2.全局展开,但是一般情况下不建议全局展开
3.部分展开
下面我们用代码展示一下三种不同的访问方式:
#include <iostream>
#include "Queue.h"
using namespace std;
// 1、指定命名空间访问
// 2、全局展开. 一般情况,不建议全局展开的。
// 3、部分
int main()
{
struct Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
std::cout << "1111";
return 0;
}
#include <iostream>
using namespace std;
// 实际开发的项目工程
// 1、指定命名空间访问
// 3、常用部分展开
// 小的程序,日常练习,不太会出现冲突
// 2、全局展开.一般情况,不建议全局展开的。
// 常用展开
using std::cout;
using std::endl;
int main()
{
cout << "1111" << endl;
cout << "1111" << endl;
cout << "1111" << endl;
cout << "1111" << endl;
int i = 0;
std::cin >> i;
return 0;
}
3.C++的输入和输出
#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
cout<<"Hello world!!!"<<endl;
return 0;
}
说明:
1.使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含头文件以及按命名空间使用方法使用std;
-
cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含< iostream >头文件中。
-
<<流插入运算符,>>流提取运算符。
-
C++输入输出可以自动识别变量类型。
注意:早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应头文件即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,规定C++头文件不带.h;旧编译器(vc 6.0)中还支持<iostream.h>格式,后续编译器已不支持,因此推荐使用+std的方式。
#include <iostream>
using namespace std;
int main()
{
int a;
double b;
char c;
// 可以自动识别变量的类型
cin>>a;
cin>>b>>c;
cout<<a<<endl;
cout<<b<<" "<<c<<endl;
return 0;
}
关于cout和cin还有很多更复杂的用法,比如控制浮点数输出精度,控制整形输出进制格式等等。
std命名空间的使用惯例:
std是C++标准库的命名空间,如何展开std使用更合理呢?
- 在日常练习中,建议直接using namespace std即可,这样就很方便。
- using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模大,就很容易出现。所以建议在项目开发中使用,像std::cout这样使用时指定命名空间 + using std::cout展开常用的库对象/类型等方式。
4.缺省参数
4.1缺省参数的概念
缺省参数是
声明或定义函数时
为函数的
参数指定一个缺省值
。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
void Func(int a = 0)
{
cout<<a<<endl;
}
int main()
{
Func(); // 没有传参时,使用参数的默认值,即a=0;
Func(10); // 传参时,使用指定的实参
return 0;
}
4.2缺省参数的分类
4.2.1全缺省参数
// 全缺省
void Func(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
cout << endl;
}
4.2.2半缺省参数
void Func(int a, int b = 10, int c = 20)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
特别注意:
-
半缺省参数必须
从右往左依次
来给出,不能间隔着给。
从右往左依次给出半缺省参数是指不能出现以下情况:
void Func(int a = 10, int b , int c = 20) //这种情况就不符合从右往左依次给出,是错误的
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
2.缺省参数不能在函数声明和定义中同时出现
//a.h 函数的声明
void Func(int a = 10);
// a.cpp 函数的定义
void Func(int a = 20)
{}
// 注意:如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。
-
缺省值必须是常量或者全局变量
-
C语言不支持(编译器不支持)
#define _CRT_SECURE_NO_WARNINGS #include<iostream> struct Stack { int* a; int top; int capacity; }; void StackInit(struct Stack* ps, int defaultCapacity = 4) //设置缺省值 { ps->a = (int*)malloc(sizeof(int) * defaultCapacity); //我们为栈开辟空间 if (ps->a == NULL) { perror("malloc fail"); exit(-1); } ps->top = 0; ps->capacity = defaultCapacity; } int main() { Stack st1; // 最多要存100个数 StackInit(&st1, 100);//我们指定大小为100,那么我们就开辟指定的100个空间 Stack st2; // 不知道多少数据 StackInit(&st2);//我们不知道开多大空间,那么就不指定,直接利用缺省参数,开辟4个int大小的空间 return 0; }
5.函数重载
5.1函数重载的概念
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数或者类型或者类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
同名函数的形参列表不同:
1.参数类型不同
// 1、参数类型不同
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
2.参数个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
3.参数类型顺序不同
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}
int main()
{
Add(10, 20);
Add(10.1, 20.2);
f();
f(10);
f(10, 'a');
f('a', 10);
return 0;
}
相比较C语言来说,C++有函数重载,重载的情况如上,但是重载的原理是什么呢?
5.2C++支持函数重载的原理 (名字修饰 —–name Mangling)
C语言不存在函数重载,C++才有函数重载,我们都知道函数的调用转到汇编代码就可以看到是一个个指令完成的,我们的C语言是根据函数名来查找的,即便满足上述函数重载的三个条件但是还是会报错,但是C++会对函数名字进行修饰,我们举个例子。因为在VS下C++函数名字修饰很复杂,所以我们在Linux操作系统下利用gcc和g++来分析:
如下是我们假设出来的函数:func1函数
我们来看一下在Linux操作系统下函数名是怎么样的
我们可以清楚的发现,C语言转成汇编代码函数名字还是func1,我们再来分析一下c++的情况:
同样的函数在两种情况下编译器编译完成后函数名竟然不一样了,C++的添加了一些符号,我们来分析一下这些符号都是什么意思?
最前面的_Z我们就当作默认的规则即可,后面的5表示我们的函数名有5个字符,函数名后面的id分别表示第一个形参是int型,第二个形参是double类型。所以我们就明白了C++有函数重载的原理,同时了解了Linux环境下C++名字修饰的含义。
但是还有一个易错点,如果我们函数的返回值类型不同会构成函数重载吗?
答案是错误的,这种情况不是函数重载,有人说因为C++名字修饰里面没有返回值,这个答案不完全对,本质是因为会造成函数调用的误解,我们进行函数调用的时候是不需要添加函数的返回值类型的,我们直接调用,如果两个函数的形参列表都相同唯独返回值不同,编译器也不知道该调用那个函数,就会报错,所以函数的返回值类型不同是不可以构成函数重载的。
6.总结
我们对C++第一讲做一下总结:首先我们学习了C++的关键字,不要求全部背诵下来,但要对常用的要做到牢记于心,同时我们学习了C++的命名空间,这是一个全新的概念,在C语言中是没有这种概念的,我们深入了解了命名空间的使用和原理,之后我们学习了C++的输入和输出,即cin和cout,再接下来我们学习了缺省参数的相关知识,最后我们了解了一个全新的概念——函数重载,并且在Linux环境下分析了函数重载的原理,希望可以帮助到大家~