C++后端

  • 熟悉C/C++,熟练使用C/C++的指针应用及内存管理,C++的封装继承多态,STL常用容器,C11常用特性(智能指针等) ,了解 Python,Gtest等。
  • 熟悉常用设计模式(单例模式,工厂模式等)
  • 熟悉Linux下vim开发环境,了解网络编程,IO多路复用,epoll等等。
  • 熟悉OSI五层网络模型,熟悉TCP/IP,UDP,HTTP/HTTPS,DNS等网络协议,熟悉TCP三次握手,四次挥手,流量控制,拥塞控制等手段。
  • 熟悉操作系统的进程通信、死锁、内存管理等知识。
  • 熟练使用MySQL,熟悉MySQL索引、事务、存储引擎、锁机制。
  • 熟悉常用的数据结构(链表、栈、队列、二叉树等),熟练使用排序,贪心,动态规划等算法。
  • 熟悉Git,vscode工具使用。

C指针应用及内存管理

指针应用

指针存放某个对象的地址,其本身就是变量,本身就有地址,所以有指向指针的指针;可变,包括其指向的地址的改变和其指向的地址中所存放的数据的改变。

引用和指针的区别(拓展)

引用就就是变量的别名,从一而终,不可变,必须初始化。
不存在指向空值的引用,但是存在指向空值的指针。

内存管理

内存一般被划分为四个区:堆区、栈区、数据区、代码区。
堆区:堆从低地址向高地址增长,先进先出,存储局部变量、函数参数值。
栈区:栈从高地址向低地址增长,先进后出,动态申请内存用。
数据区:存放全局变量、静态变量、常量。
代码区:存放代码数据。

malloc动态分配内存

  • malloc接收一个输入参数,表示所需分配内存的大小,以字节为单位。
  • 检查是否有足够的内存可供分配。
  • 如果有足够的可用内存,malloc会分配一块连续的内存空间,返回分配的内存块的起始地址,否则返回NULL。

new和malloc(拓展)

  • new内存分配失败,会抛出bac_alloc异常,不会返回NULL;而malloc分配失败会返回NULL。
  • 使用new申请内存无需指定内存块的大小,malloc需要显式指出所需内存的尺寸。
  • new和delete可以重载,而malloc/free不允许重载。
  • new/delete会调用对象的构造函数/析构函数来完成对象的构造/析构,而malloc不会初始化对象。
  • malloc/free是C++/C语言的标准库函数,而new/delete是C++的运算符。
  • new从自由存储区为对象动态分配内存空间,malloc在堆上动态分配空间。

C++的封装、继承、多态

继承

定义:让某种类型对象获得另一个类型对象的属性和方法。
功能:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
常见继承的三种方式:

  1. 实现继承:使用基类的属性和方法无需额外编写代码。
  2. 接口继承:仅使用基类的属性和方法的名称,但是子类必须提供实现的能力(虚函数)。
  3. 可视继承:指⼦窗体(类)使⽤基窗体(类)的外观和实现代码的能⼒。

封装

定义:数据和代码捆绑在一起,避免外界干扰和不确定性访问。
功能:把客观事物封装成抽象的类,并且可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏,例如,公共的数据或方法使用public修饰,不希望被访问的数据或方法采用private修饰。

多态

定义:同一事物表现出不同事物的能力,即向不同对象发送同一条消息,不同的对象在接收时会产生不同的行为(重载实现编译时的多态,虚函数实现运行时的多态)
功能:多态性是允许你将父对象设置成为和一个或更多的它的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作;
简单一句话:允许将子类类型的指针赋值给父类类型的指针。允许用子类类型的变量去初始化父类类型的引用。
实现多态的两种方式:

  1. 覆盖:指子类重新定义父类的虚函数的做法
  2. 重载:指存在多个重名函数,而这些函数的参数表不同(体现在个数或类型上)

STL常用容器

  • vector:动态数组,支持快速随机访问,可以在尾部高效地插入和删除元素。
  • list:双向链表,支持在任意位置高效地插入和删除元素,但不支持随机访问。
  • deque:双端队列,类似于动态数组,支持在两端高效地插入和删除元素。
  • queue:队列,基于其他容器实现,支持先进先出(FIFO)的元素访问方式。
  • stack:栈,基于其他容器实现,支持后进先出(LIFO)的元素访问方式。
  • set:集合,存储唯一元素,自动按照一定顺序(默认是升序)组织元素。
  • map:映射,存储键-值对,键是唯一的,按照键的一定顺序(默认是升序)组织元素。
  • unordered_set:无序集合,类似于set,但不保证元素的顺序。
  • unordered_map:无序映射,类似于map,但不保证元素的顺序。
  • multiset:多重集合,允许存储多个相同值的元素。
  • multimap:多重映射,允许存储多个相同键的键-值对。

智能指针

C++中的智能指针是一种用于管理动态分配的内存资源的指针类型。它们帮助自动管理内存,避免内存泄漏和悬挂指针等问题。
标准C++库(std)提供了三种主要的智能指针类型:

  1. std::unique_ptr:
    std::unique_ptr是独占所有权的智能指针,它确保在其生命周期结束时自动释放所管理的对象。每个std::unique_ptr实例只能拥有一个对象,不能进行复制,但可以进行移动。通常用于表示独占的资源,例如动态分配的对象。

     #include <memory>
    
     std::unique_ptr<int> uniqueIntPtr = std::make_unique<int>(42);
    
  2. std::shared_ptr:
    std::shared_ptr是一种共享式智能指针。它允许多个 std::shared_ptr实例共享对同一个资源(堆上的对象)的所有权。资源会在最后一个std::shared_ptr实例销毁时释放。使用std::make_shared来创建。

     #include <memory>
    
     std::shared_ptr<int> ptr1 = std::make_shared<int>(42);
     std::shared_ptr<int> ptr2 = ptr1; // 共享所有权
    
  3. std::weak_ptr:
    std::weak_ptr也是一种共享式智能指针,但它并不增加资源的引用计数。它允许访问由std::shared_ptr管理的资源,但不会影响资源的所有权。通常用于避免std::shared_ptr之间的循环引用,从而防止内存泄漏。

     #include <memory>
    
     std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
     std::weak_ptr<int> weakPtr = sharedPtr; // 不增加资源引用计数
    

单例模式和工厂模式

工厂模式是一种创建型设计模式,旨在将对象的创建与使用分离,以便于系统的扩展和维护。它包括一个抽象工厂接口和多个具体工厂类,每个具体工厂类负责创建一类具体的产品对象。客户端代码只与抽象工厂和产品接口交互,而不与具体产品类直接交互。
工厂模式的优点:

  • 对象创建与使用分离:工厂模式将对象的创建与客户端代码分离,使得客户端代码不需要关心具体的对象创建细节,从而降低了耦合性。
  • 易于扩展:当需要添加新的产品类时,只需要创建一个新的具体产品类和对应的工厂类,而不需要修改已有的代码,符合开闭原则。
  • 隐藏复杂性:如果对象的创建逻辑很复杂,工厂模式可以将这些复杂性封装在工厂内部,使客户端代码更加简洁。
  • 统一管理:工厂模式集中管理对象的创建,便于监控和维护。
    工厂模式的缺点:
  • 增加类的数量:引入了工厂类和具体产品类,增加了代码的数量。
  • 引入间接性:由于需要通过工厂创建对象,增加了代码的间接性,可能会稍微增加阅读和理解的难度。

单例模式是一种创建型设计模式,旨在确保一个类只有一个实例,并提供一个全局访问点来访问该实例。它常用于需要全局共享、全局状态一致或避免多次实例化的情况。
单例模式的优点:

  • 全局唯一实例:确保一个类只有一个实例,避免了多个实例之间的冲突和重复创建。
  • 延迟实例化:只有在第一次需要时才会创建实例,节省了资源。
  • 全局访问点:通过单例模式可以方便地获取全局唯一的实例。
    单例模式的缺点:
  • 可能引入全局状态:由于单例是全局唯一的,可能在某些情况下引入全局状态,增加了代码的复杂性。
  • 可能影响并发性能:在多线程环境下,需要考虑线程安全性,可能需要额外的同步措施,可能会影响性能。
  • 隐藏依赖关系:单例模式可能隐藏了类之间的依赖关系,使代码结构不够清晰。

综上所述,工厂模式适用于对象创建比较复杂、需要灵活扩展的情况下,而单例模式适用于需要确保全局唯一实例以及全局访问点的情况下。选择使用哪种模式取决于具体的应用场景和设计需求。