🪐🪐🪐欢迎来到程序员餐厅💫💫💫
今日主菜:c++入门
主厨:邪王真眼
所属专栏:c++专栏
主厨的主页:Chef‘s blog
前言:
咱也是好久没有更新了,继c语言和数据结构之后,今天再开个新坑c++,学c++与C语言有个共同点,那就是要及时写博客,一方面是及时总结知识方便以后回顾复习,一方面也是检验自己当前的学习效果,欧克,那麽今天我们就要踏入c++的大门,今天先来初窥门径。
1. 本节要求
C++ 是在 C 的基础之上,容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式
等。熟悉 C 语言对 C++ 学习有一定的帮助,
本节主要目标:
- 1. C++是如何对C语言设计不合理的地方进行优化的,比如:作用 域方面、IO方面、函数方面、指针方面、宏方面等。
- 2. 为后续类和对象学习打基础。
2. C++关键字(C++98)
C++ 总计 63 个关键字, C 语言 32 个关键字
我们现在只是看一下 C++ 有多少关键字,不对关键字进行具体的讲解。他们的具体作用后面我们学到一个讲一个。
3. 命名空间
在 C/C++ 中,变量、函数和后面要学到的类都是大量存在的,将这些变量、函数和类的名称将都存
在于全局作用域中,可能会导致很多命名冲突,比如不小心给两个函数起了一样的名字。c++是通过使用命名空间来解决这个问题的。
这里就要用namespace 关键字了。(打起精神,第一个关键字来了!!!)
3.1样例
C语言命名冲突如下:
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
//我们知道C语言有个叫rand的函数,我们在这里又定义了一个叫rand的变量。
int main()
{printf("%d\n", rand);
return 0;
}
// 编译后后报错:error C2365: “rand”: 重定义;以前的定义是“函数”
3.2 命名空间定义
定义命名空间,需要使用到 namespace 关键字 ,后面跟 命名空间的名字 ,然 后接一对 {} 即可, {}
中即为命名空间的成员。
// sixflower是命名空间的名字。
// 1. 正常的命名空间定义
namespace sixflower
{// 命名空间中可以定义变量/函数/类型int rand = 10;int Add(int left, int right){return left + right;}
注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中
3.3 命名空间使用
命名空间中成员该如何使用呢?比如:
namespace bit
{// 命名空间中可以定义变量/函数/类型int a = 0;int b = 1;int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};
}
int main()
{// 编译报错:error C2065: “a”: 未声明的标识符printf("%d\n", a);
return 0;
}
命名空间的使用有三种方式:
3.3.1.加命名空间名称及作用域限定符
int main (){printf ( "%d\n" , N::a );return 0 ;}
3.3.2.使用using将命名空间中某个成员引入
using N::b ;int main (){printf ( "%d\n" , N::a );printf ( "%d\n" , b );return 0 ;}
3.3.3.使用using namespace 命名空间名称引入
using namespce N ;int main (){printf ( "%d\n" , N::a );printf ( "%d\n" , b );Add ( 10 , 20 );return 0 ;}
3.4使用原理:
编辑器在找你写的变量,函数,类等时默认在当前域和全局变量中寻找,
对于方案一:其实就是告诉编译器这个a在N里,那么编译器就回去N这个域中找
对于方案二:也是告诉编译器这个b是在N里,与方案一的区别是在之后使用时不用再写为N::b的模式。
对于方案三:就是在默认的基础上,让编译器搜索范围又增加了N域
4. C++输入&输出
请看代码:
#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
cout<<"Hello world!!!"<<endl;
return 0;
}
相信你也明白这就是c++的“hello world”,但为什么这样写还听我细细道来。
- 1. 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件
- 以及按命名空间使用方法使用std。
- 2. cout和cin是全局的流对象,cout作用类似于printf,cin作用类似于类似于scanf。endl是特殊的C++符号,现在可以将它看作‘\n’,他们都包含在包含<iostream >头文件中。
- 3. <<是流插入运算符,>>是流提取运算符,
- 4. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。 C++的输入输出可以自动识别变量类型。
- 注意:早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应 头文件即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,规定C++头文件不带.h;旧编译器(vc 6.0)中还支持<iostream.h>格式,后续编译器已不支持,因此推荐使用<iostream>+std的方式。
ps:
现在搞不懂cout,cin,>>,<<是正常的,先知道它的作用就行,以后还会细讲。
#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;
}
// ps:关于cout和cin还有很多更复杂的用法,比如控制浮点数输出精度,控制整形输出进制格式等
等。因为C++兼容C语言的用法,可以用c语言实现这些要求,这些又用得不是很多,我们这里就不展开学习了。后续如果有需要,我
们再配合文档学习。
std命名空间的使用惯例:
1. 在日常练习中,建议直接 using namespace std 即可,这样就很方便。
2. using namespace std 展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型 / 对
象 / 函数,就存在冲突问题。该问题在项目开发中代码较多、规模 大,就很容易出现。所以建议在项目开发中使用方案1,2.
5. 缺省参数
5.1 缺省参数概念
缺省参数是 声明或定义函数时 为函数的 参数指定一个缺省值 。在调用该函数时,如果没有指定实
参则采用该形参的缺省值,否则使用指定的实参。
void Func(int a = 0)
{cout<<a<<endl;
}
int main()
{Func(); // 没有传参时,使用参数的默认值Func(10); // 传参时,使用指定的实参
return 0;
}
5.2 缺省参数分类
5.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;}
5.2.2半缺省参数
void Func(int a, int b = 10, int c = 20){cout<<"a = "<<a<<endl;cout<<"b = "<<b<<endl;cout<<"c = "<<c<<endl;}
1. 半缺省参数必须 从右往左依次 来给出,不能间隔着给
2. 声明与定义分开时 缺省参数不能在函数声明和定义中同时出现,只能在声明出现
//a.hvoid Func(int a = 10);// a.cppvoid Func(int a = 20){}// 注意:如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该
用那个缺省值。
3. 缺省值必须是常量或者全局变量
5. 函数重载
自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重
载了。
比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个
是男足。前者是 “ 谁也赢不了! ” ,后者是 “ 谁也赢不了! ”
5.1 函数重载概念
函数重载:是函数的一种特殊情况,C++ 允许在 同一作用域中 声明几个功能类似 的同名函数,这
些同名函数的形参列表 ( 参数个数 或 类型 或 类型顺序 ) 不同,常用来处理实现功能类似数据类型
不同的问题
#include<iostream>
using namespace std;
// 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;
}
5.2 C++支持函数重载的原理--名字修饰(name Mangling)
为什么 C++ 支持函数重载,而 C 语言不支持函数重载呢?
在 C/C++ 中,一个程序要运行起来,需要经历以下几个阶段: 预处理、编译、汇编、链接 。
这个知识忘了的可以看一下这篇博客:
编译与链接
1. 实际项目通常是由多个头文件和多个源文件构成,而通过 C 语言阶段学习的编译链接,我们
可以知道,【当前 a.cpp 中调用了 b.cpp 中定义的 Add 函数时】,编译后链接前, a.o 的目标
文件中没有 Add 的函数地址,因为 Add 是在 b.cpp 中定义的,所以 Add 的地址在 b.o 中。那么
怎么办呢?
2. 所以链接阶段就是专门处理这种问题, 链接器看到 a.o 调用 Add ,但是没有 Add 的地址,就
会到 b.o 的符号表中找 Add 的地址,然后链接到一起 。
3. 那么链接时,面对 Add 函数,链接接器会使用哪个名字去找呢?这里每个编译器都有自己的
函数名修饰规则。
4. 通过下面我们可以看出 gcc(也就是支持C语言) 的函数修饰后名字不变。而 g++(也就是支持c++) 的函数修饰后变成【 _Z+ 函数长度 + 函数名 + 类型首字母】。 采用 C 语言编译器编译后结果
5. 通过这里就理解了 C 语言没办法支持重载,因为同名函数没办法区分。而 C++ 是通过函数修
饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载 。
结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参
数类型信息添加到修改后的名字中。
注意: 如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时函数时编译器没办法直到返回值,所以无法进行区分。
结语:
事实上,这篇博客只完成了本节目标的一半,敬请期待c++入门你需要知道的知识点(下)