基础算法--C++基础回顾

基础算法–C++基础回顾

开始之前先说明一下本篇文章的定位,如果你已经掌握如下内容可以跳过本篇文章

  • 尝试梳理一些C++语言特性
  • 最终希望阅读者能够尝试自己梳理更多的C++语言特性、或用同样的思路学习其他语言

C++的功能非常强大,特性也非常多,但是限于篇幅问题,这里不能面面具到。下面只是简单帮大家回顾一些基本特性,如果想系统的学习C++这里还是推荐大家选修一门C++编程的课程或是读一本C++编程的书

关于内存

  • 内存是什么

    • 我们可以这样简单的想象内存条
      • 它是一条非常非常长的纸带,每个格子可以填写0~255的一个状态(80/1比特,一个字节)
      • 例如16G的内存,一共能填写16 * 1024 * 1024 * 1024个字节
      • 然后这个纸带卷啊卷,卷成一根内存条这么大
      • 这就是操作系统所拥有的内存资源,操作系统会将内存分配给正在执行的程序
    • 我们可以认为,程序处理的数据存储在内存中
    • 现在常用的内存条,包含若干内存颗粒(半导体集成电路)
    • 物理上,通过一些微小的元器件来表示0 1状态
    • 能存储的比特数取决集成电路里的元器件数目
  • 内存如何分配给程序

    • 内存分配的细节与编译器,操作系统,运行时环境等等有关。这里只给出一些概念上的初略过程
      • 计算机上有多个程序同时运行,操作系统也会预留一部分内存,而内存是有限的。因此,程序只能在操作系统分配给它的范围内使用内存
      • 操作系统一开始就会给程序分配一些内存,用来存储全局变量,局部变量,函数参数返回值、程序代码等。其中,全局变量、程序代码分配在static内存区域(程序从开始到结束,这些内存都会被占用)。局部变量、函数参数返回值等,被分配在栈内存区域(函数调用栈)
      • 函数被调用一次时,在函数调用栈中分配一个大小合适的栈桢,存储这一次的局部变量,参数和返回值。从函数中返回时,释放栈桢的内存(在操作系统看来,整个函数调用栈还是在程序那里)
      • 另外,程序在运行时,可以向操作系统动态地申请和释放一些内存(堆内存)
  • 变量/指针/引用

    • 变量:一块具有类型的内存(类型:数据的存储表示方式以及你可以对它进行的操作)
    • 指针:一块内存的地址,指针的类型可能说明这个指针指向特定类型的变量
    • 引用:可以理解为指针的一种“语法糖”(左值引用/右值引用)
    • 数组:内存中连续排列的多个同类型变量。数组名称可以用作指向第一个元素指针
    • 自定义的类型(class/struct):一组成员变量在内存里面的排列方式以及可以对它进行的操作(这里可以了解一下struct中变量的排布等)
    • 一个对象:按照特定排列方式存储在内存里的一组成员变量
  • 构造函数/析构函数

    • C++中两个运算符号new/delete替代了C语言的malloc/dealloc库函数
    • 通过new/delete动态分配或释放一个对象会发生
      • new分配内存,然后调用对应的构造函数(递归调用各个成员变量的构造函数)
      • delete调用对应的析构函数,然后释放内存
    • 动态内存管理的两种风格
      • Resource Acquisition Is Initialization (RAII)资源获取即初始化(C++语言中可通过恰当实现构造/析构函数、恰当调用new/delete)
      • 垃圾回收(C++语言中可通过智能指针实现)

关于函数

  • 减少函数调用(内联函数/预处理宏)

    • 调用函数时,处理参数/返回值/栈桢的产生和销毁会带来一定的开销。因此,对简单的函数,将调用函数改为直接嵌入一段代码,可以节约一些计算开销
      • C语言采用宏定义,#define min(a, b) ((a < b) ? a : b)
      • C++使用inline关键字建议编译器进行内联(但并不代表编译器一定会这么做)
      • C++中建议非必要不使用宏
  • 传值与传引用

    • 传值,将变量拷贝一份传递给函数,函数里面和函数外面是两份数据
    • 传引用,只是将变量的引用传递给函数,函数里面和函数外面是同一份数据
  • 浅/深拷贝

    • 当一个类中包含动态分配的资源时
      • 浅拷贝:不会分配第二份资源,使得拷贝后的对象和之前的对象指向同一份数据(如:数组int a[10];int b[10] = a;)。这其实在很多时候是一个bug,如果真的需要这样使用,应该采用CoW/Move来代替
      • 深拷贝:对所有成员变量逐个拷贝。合理的拷贝行为应该满足:等价性、独立性
  • 拷贝/移动

    • 有时并不需要进行时拷贝,因为在完成拷贝之后,旧的元素就失去了使用价值

    • 可以通过move来避免不必要的拷贝(右值引用表示一个可以被销毁的临时值)

      template<class T>
      void swap(T &a, T &b) {
          const T tmp = a;    // put a copy a into tmp
          a = b;              // put a copy of b into a
          b = tmp;            // put a copy of tmp into b
      }
      
      template<class T>
      void swap(T &a, T &b) {
          T tmp = std::move(a);   // std::move 会返回一个右值引用
          a = std::move(b);   
          b = std::move(tmp);
      }
      
  • 函数指针

    • 通过函数指针,可以将函数作为一个参数传递给要调用的函数或者说传入一个谓词
    • 实质上是函数的代码所在的地址
    • 可以列举两个例子
      • 给排序函数出入一个比较函数的函数指针作为参数
      bool cmp_func(const int &a, const iny &b) { 
          retutn abs(a) < abs(b); 
      }
      std::sort(array_a.begin(), array_a.end(), cmp_func);
      
      • 设置回调函数(比如:操作系统中的中断向量表)
  • 函数对象

    • 函数对象是重载了函数的运算符()的对象
    • C++中,应当倾向于使用函数对象或lambda表达式而非函数指针
      struct cmp { 
      	bool operator()(const int &a, const int &b) {
      	 retutn a < b; 
      	}
      }
      
  • Lambda表达式

    • C++11标准中引入的匿名函数,用于更加方便定义一个匿名函数对象
    • 可以在lambda中`捕获当前作用域的变量,定义参数列表,也可以有返回值

关于多态

  • 虚函数

    • 在成员函数前标注virtual,子类可以重新实现这个函数,编译器和运行时环境通过虚表保证调用正确版本的函数
    • 纯虚函数要求子类必须重新实现这个函数
    • 虚表可以认为是子类隐藏的一个成员数组,数组中标注每个虚函数具体指向哪一个实现版本(通常是继承关系上,最近的一个类所实现的版本,例如如果这个类自身有实现,就调用自身实现的版本,如果自身没有实现,就会调用向上查找)(虚表中可能会保存指向一些函数实现的函数指针)
  • 多态

    • 通过继承和虚函数,可以实现这样的行为。某个类的类型的指针,它可以指向某个子类的对象,并正确调用子类对虚函数的具体实现
  • 多态的实现

    • 通过继承、虚函数可以实现运行时多态
      • 我们将一个子类作为参数传递给一个函数,函数如果不用指针和引用而是直接使用一个对象来接收这个参数,就有可能导致意外的切片,因为这里存在一个隐士转换,将子类转换为基类,丢了子类的数据
    • 通过模版也可以实现编译时多态
  • 模版简介

    • 函数:对于两段只有参数值不同的代码,不用重复编写
    • 模版:对于两个只有参数类型不同的函数,不用重复编写,编译器自动生成程序中用到的不同类型的函数。模版较多的代码往往编译起来非常慢,最后的编译的机器码也会比较大
  • 为什么模版类的函数声明和定义要放在一起

    • 从模版代码生成的过程考虑:
      • 对于编译器来说,模版本身并不是一个能直接拿来链接的函数,而是需要利用模版来生成一些其他的函数
      • 将函数声明和定义拆开编写,其实是在链接阶段再去处理函数名称和函数实现的绑定
      • 链接器通常没有办法在链接阶段再去处理模版参数的替换
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虎小黑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值