💒前言

  学习本章内容需要掌握C语言{类型、循环、函数、结构体、指针}等内容 建议学完C再看
  希望对从来没有接触过C++ 只是听过的小白 有些帮助
  ——我并不专业,也是正在学习C++的小学生!

🧸什么是C++

  C++是C升级而来,所以C++程序兼容C的大部分代码!

🧸初识C++代码

先看一段C的代码

#include <stdio.h>
int main()
{
	printf("hello world!");
}

  这段代码很简单,#include <stdio.h>printf函数的头文件,包含头文件,printf打印函数才可以在屏幕上打印;
  并且打印其他类型需要自己指定例如:
  int a = 10;
  printf("%d\n",a);
下面是C++的代码

#include <iostream>
using namespace std;
int main()
{
	cout <<"hello world!"<< endl;//流插入
	return 0;
}

  解释以下上面的代码,
  #include <iostream>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[i]这个空间的别名

  当函数返回值 出了函数作用域销毁 则不能使用引用做返回值

    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中空指针是 NULLNULL本质 = 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

🎀结束

  本次的入门基础 就结束啦 ,下一章 类和对象!

更多推荐

【C++】入门基础