💒前言
学习本章内容需要掌握C语言{类型、循环、函数、结构体、指针}等内容 建议学完C再看
希望对从来没有接触过C++ 只是听过的小白 有些帮助
——我并不专业,也是正在学习C++的小学生!
🧸什么是C++
C++是C升级而来,所以C++程序兼容C的大部分代码!
🧸初识C++代码
先看一段C的代码
- #include <stdio.h>
- int main()
- {
- printf("hello world!");
- }
复制代码
这段代码很简单,#include 是printf函数的头文件,包含头文件,printf打印函数才可以在屏幕上打印;
并且打印其他类型需要自己指定例如:
int a = 10;
printf("%d\n",a);
下面是C++的代码
- #include <iostream>
- using namespace std;
- int main()
- {
- cout <<"hello world!"<< endl;//流插入
- return 0;
- }
复制代码
解释以下上面的代码,
#include 是C++的库,io意为输入输出;stream意为流。
——————可以称之为 输入输出流
using namespace std;要解释这句 ,你需要先知道命名空间这个功能,下面会讲
cout <<"hello world!"<< endl; ,cout:流插入;endl:换行符;
此句意为 将"hello world!"字符串流向cout ,也就打印在了屏幕上,后面是将 endl 流向cout; 后面会详解
🧸命名空间
关键字:namespace
命名空间:可以理解为 变量、函数 的名字的作用域
在C++的库中 都是将函数的实现 封装起来 为了防止命名冲突
用法
- namespace N
- {
- int a = 10;
- int Add(int left, int right)
- {
- return left + right;
- }
- }
- int main()
- {
- printf("%d\n", a); // 该语句编译出错,无法识别a
- return 0;
- }
复制代码
上述代码 是无法识别到a的,因为a 不在main函数中,也不再全局变量中,而是封装在N的命名空间中
main函数找a的顺序:先找局部,再找全局
如何找到a
:: 作用域运算符
在a前面加上命名空间 N和作用域运算符::
- int main()
- {
- printf("%d\n", N::a);
- }
复制代码
展开命名空间
关键字:using
展开后 命名空间失效 ,内部代码全部暴露
此时可以解释using namespace std;是什么意思
展开名字叫std的命名空间,因为不展开 所有的对象都用不了
也可以展开部分对象
using std::cin; //展开cin - 流提取,接收变量的 与scanf作用相同
using std::cout; //展开cout - 流插入 ,打印变量的 与printf作用相同
cout、cin是一个ostream类的对象
对象
类似C中的结构体,在C++中叫对象
🧸输入、输出
你可以复制这段代码 测试一下
- #include <iostream>
- using namespace std;
- int main()
- {
- int a;
- cin>>a;
- cout<<a<<endl;
- return 0;
- }
- 输入 a 的数值,并打印 ,在c++中打印会自动识别类型
复制代码
🧸缺省参数
在定义函数时 可以提前给定数值,传参时可以少传参数、或者不传参数
- void TestFunc(int a = 0)
- {
- cout<<a<<endl;
- }
- int main()
- {
- TestFunc(); // 没有传参时,使用参数的默认值
- TestFunc(10); // 传参时,使用指定的实参
- }
复制代码
全缺省:多个函数参数 都有缺省参数
- void TestFunc(int a = 10, int b = 20, int c = 30)
复制代码
半缺省:从右向左依次给缺省参数
- void TestFunc(int a, int b = 10, int c = 20)
复制代码
缺省参数不能在函数声明和定义中同时出现
问题: 如果是以下程序 会如何调用?
- void Test(int a = 0)
- {
- cout << a << endl;
- }
- void Test()
- {
- cout << 10 << endl;
- }
- int main()
- {
- Test();
- return 0;
- }
复制代码
上述代码中出现了两个Test,构成函数重载,在C中不支持,在C++中支持 ,函数重载则可以解释
先说结论:报错 Test 对重载函数的调用不明确
如果没看懂,先看下面的,再回来看。
🧸函数重载
同意作用域 可以有多个重名函数,条件是:参数个数、类型、顺序,必须不同
返回类型 不同 不构成函数重载
- void Test(double a)
- {
- cout << a << endl;
- }
- void Test(int a)
- {
- cout << a << endl;
- }
- int main()
- {
- Test(1.1);
- Test(1);
- return 0;
- }
复制代码
上述代码,会根据给定的实参去找对应的函数,复制代码试一试
原理
C不支持函数重载是因为C语言对函数的命名规则比较简单
C++在Windows下的规则过于复杂,不便观看,下面用linux演示
- int Add(int a,double b,int* p)
复制代码
对于以上代码 C的汇编 函数名Add
C++的汇编函数名 _Z3AddidPi
_Z 前缀
3 函数名个数
Add 函数名
i int类型
d double类型
Pi int*类型
回顾编译器,编译的过程
1、预处理:头文件展开、宏替换、条件编译、去掉注释,生成.i文件
2、编译:检查语法、生成汇编代码,生成.s文件
3、汇编:汇编代码转换成二进制机器码 ,生成.o文件
4、链接:生成.out文件
如果文件中有 函数的实现 则函数产生的名字 直接带有汇编实现地址
如果文件中只有函数声明,定义在顶一个cpp文件中,则连接的时候,到.o生成的符号表里找到函数的定义的地址,才会填写函数的地址
🧸引用
引用符号:&
引用:变量的别名
用法:Type& name;
引用概念
例如:一只修勾叫多多,它原来叫 狗狗 ,起了个名字 叫多多,本质上 都是同一只修勾!
- void TestRef()
- {
- int a = 10;
- int& b = a;//<====定义引用类型
- printf("%p\n", &a);
- printf("%p\n", &b);
- }
复制代码
可以看到 a和b的地址相同 值相同,证明b就是a,那有什么用呢? 后面详解。
引用特性 1、必须初始化 int& b; 这样写是不对的
2、一个变量可以有多个引用,int a ; int &b = a; int& c = b; int& d = a;可以同时存在
3、引用只能引用一个实体 int a; int c ; int & b = a; 这样没问题 在写一个int& b = c;则错误,不能改变。
引用使用场景
1、做参数 :a.提高效率 b.形参修改,改变实参
- void Swap(int& left, int& right)
- {
- int temp = left;
- left = right;
- right = temp;
- }
- 此时,形参改变会影响 实参,因为形参是实参的引用,实际上就是实参,而且不会产生临时变量。
复制代码
2、做返回值 :a.提高效率 b.修改返回变量
提高效率 是因为,不管事传值,还是传址,都会产生临时变量而拷贝,而引用则不会产生临时拷贝,从而减少工作量
- #include N 10;
- int& At(int i)
- {
- static int a(N);
- return a[i];
- }
- for(size_t i = 0;i<N;i++)
- {
- At(i) = 10+i;
- }
复制代码
加上关键字static,该变量就被定义成为一个静态全局变量
此时a是静态变量 不会被销毁 而且返回值是 左值 可以被修改 反回了a这个空间的别名
当函数返回值 出了函数作用域销毁 则不能使用引用做返回值
- int& Add(int a,int b )
- {
- int ret = a+b;
- return ret;
- }
复制代码
此时 函数调用完ret会被回收,可能会造成非法访问
引用权限
- 权限放大 - 不行
- const int a = 10;
- int& b = a;
-
- 权限不变 - 可以
- const int c = 20;
- consr int& d = c;
-
- 权限缩小 - 可以
- int e = 30;
- const int& f = e;
-
- `注:`表达式产生的结果是 具有常属性的 所以想要引用则需要加const
- int x1 = 1, x2 = 2;
- const int& y = x1+x2; 可以
-
- 包括整形提升、截断 所产生的的是 临时拷贝的整形提升、截断
-
- double d = 11.11;
- int a = d;
- a = 11 是 d 的临时拷贝 进行截断产生的
- int& i2 = d; 不可以 - 因为临时拷贝有常属性不能改变
- const int& i3 = d; 可以 用const修饰后 i3则不能改变 权限不变
-
复制代码
引用和指针的区别
在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
Col1 | 引用(渣男) | 指针(专一) |
---|
意义 | 存变量的地址 | 是变量的别名 |
初始化 | 不一定要初始化 | 必须初始化 |
| 能随意指向同类型实体 | 只能引用一个实体 |
空值 | 有NULL指针 | 没有NULL引用 |
大小 | 指针大小固定 | 引用的大小是实体的大小 |
增加 | 指针增加是偏移一个类型的大小 | 引用增加是实体+1 |
总结:指针使用更为复杂 因为控制不好可能会出现野指针 空指针 ,所以没有引用安全
🧸extern “C” 编译连接C++与C的调用
例如:
1、C调用C++写的库(静态库、动态库)
2、C++调用C写的库(静态库、动态库)
实现方法待填写
…
🧸内联函数 inline
- 在C中为了 减少函数调用可以使用宏
- #define ADD(x,y) ((x)+(y))
- 来代替 ADD(int x, int y){return x+y;}
-
- 但是宏很复杂 例如控制不好括号 容易造成错误
- 有了inline 则可以代替宏
- inline void Func()
- {
- .假设编译后是10行代码
- }
复制代码
内联函数原理
可以减少函数栈帧,直接替换成代码 不建立栈帧
内联函数使用场景
建议 代码长度小于10 或20行 而且经常调用的函数
危害:
1、假设内联函数展开是10行 调用1000次 则是10*1000 = 10000行代码
2、假设函数不展开 调用1000次 则是10+1000 = 1010行代码
内联函数结论
短小,频繁调用的函数建议定义成inline
🧸auto 自动填写类型
auto在类型特别长的时候用起来很好,或者在下面的范围for循环中可以用
- int a = 0;
- auto b = a;
- auto c = 'c';
- auto d = 1.1;
复制代码
🧸基于范围的for循环(C++11)
在C中循环这样写
- int main()
- {
- int a[] = {1,2,3,4,5,6,7,8,9};
- int n = sizeof(a) / sizeof(a[0]);
- for(int i = 0;i < n ;i++)
- {
- printf("%d ",a[i]);
- }
- return 0;
- }
复制代码
在C++中的范围for这样写
- int main()
- {
- int a[] = {1,2,3,4,5,6,7,8,9};
- for(auto i:a)
- {
- cout << i <<" ";
- }
- cout << endl;
- return 0;
- }
复制代码
运行结果如下
这里 auto 可以自动识别 i的类型 ,当然自己写int也可以
范围for赋值呢?
- int main()
- {
- int a[] = { 1,2,3,4,5,6,7,8,9 };
- for (auto& i : a)
- {
- i = 10 + i;
- }
- for (auto i : a)
- {
- cout << i << " ";
- }
- cout << endl;
- return 0;
- }
复制代码
给i添加个引用符号 ,让i等于a[] 中的元素,直接改变i即可!
范围for使用条件
下列代码错误,因为不确定a的范围
- void TestFor(int array[])
- {
- for(auto& e : array)
- cout<< e <<endl;
- }
复制代码
🧸空指针nullptr(c++11)
在C中空指针是 NULL 而NULL本质 = 0;而这样在C++中可能会产生歧义
- void f(int)
- {
- cout<<"f(int)"<<endl;
- }
- void f(int*)
- {
- cout<<"f(int*)"<<endl;
- }
- int main()
- {
- f(0);
- f(NULL);
- f((int*)NULL);
- return 0;
- }
-
- 结果为:f(int)
- f(int)
- f(int*)
- 也就是NULL 也去调用了第一个f(int),而没有调用f(NULL)
复制代码
cout << sizeof(nullptr) << endl;
cout << sizeof(void*) << endl;
结果都为4
后续使用都推荐nullptr
🎀结束
本次的入门基础 就结束啦 ,下一章 类和对象!