1、C++基础与高级
C++与C的区别
C 语言是过程式编程语言,强调函数和过程调用。C++支持面向对象、函数重载、模板、STL。
引用、指针、右值引用的区别?
引用是别名,指针可以重新赋值并可空;右值引用属于移动语义、优化拷贝。
左值:具有生命周期,即有一个具体的内存空间。++x
右值:没有生命周期,将亡值。不指向稳定的内存地址。x++
常量左值,既可以绑定左值又可以绑定右值
右值引用:指向右值的引用。使用&&表示。右值引用的主要作用支持移动语义和完美转发。避免不必要操作(copy),提供性能。可以延长右值的生命周期。
移动语义与完美转发
移动语义 = “把昂贵的深拷贝变成廉价的指针过户”
完美转发 = “参数原样快递”
借助万能引用 + std::forward,把左值继续当左值、右值继续当右值,零拷贝、零移动、零信息丢失,让模板函数像“透明管道”一样把实参送到真正干活的函数手里。
深拷贝与浅拷贝
深拷贝会开辟新空间复制内容到新空间,而浅拷贝仅指针指向内存地址。
STL容器有哪些?区别在哪?
顺序容器
- vector:动态数组,存储连续,支持随机访问,在尾部插入删除高效,适合需要随机访问,经常在尾部添加删除元素。
- deque:双端队列,动态数组。支持随机访问,在头尾插入效率为O(1),中间O(n)。与vector区别,两端高效增长,但内存不是连续的,遍历速度慢。
- list:双向链表,存储非连续。不支持随机访问,在任意节点插入都为O(1),但需要先找到位置。适合频繁在任意位置插入的场景,不用随机访问
- forword_list:单向链表,比list省空间,只能单向遍历,适合对内存有严格要求。
- array:固定大小数组,栈上分配内存,支持随机访问。
- string:专门用于存储字符动态数组,连续存储,支持随机访问,尾部高效。
关联容器
按照关键字排序,底层红黑树。
- set:存储唯一关键字,按关键字排序,(关键字唯一)去重。插入删除查找效率O(log n)
- multiset:多重集合,允许关键字重复,按关键字排序。插入删除查找效率O(log n)
- map:关联数组。存储键值对,按键排序,键唯一。插入删除查找效率O(log n)
- multimap:多重映射,允许重复键,一个键对应多个值。插入删除查找效率O(log n)
无序关联容器
- unordered_set:无序集合,存储唯一关键字,通过哈希组织,平均插入删除效率为O(1),最坏O(n)
- unordered_multiset:无序多重集合,允许重复关键字,平均插入删除效率为O(1),最坏O(n)
- unordered_map:无序映射,存储键值对,通过哈希组织,键唯一。平均插入删除效率为O(1),最坏O(n)
- unordered_multimap:无序多重映射,键重复,通过哈希组织,键唯一。平均插入删除效率为O(1),最坏O(n)
容器适配器
- stack:栈,后进先出,默认顶层为deque
- queue:队列,先进先出,默认顶层容器deque
- priority_queue:优先队列,元素按照优先级出队,默认最大堆。vector。
存储方式:
连续内存:vector、deque、array、string
链表:list、forward_list
树结构:set,multiset、map、multimap
哈希表:unordered_set, unordered_multiset, unordered_map, unordered_multimap
常用的设计模式
类与类之间的关系
- 继承关系,也叫泛化
- 关联关系:单向关联关系,双向关联关系、自关联关系(链表)。
- 聚合关系
- 组合关系
- 依赖关系
聚合与组合的区别:组合的两个对象之间生命周期有很大关系,被组合的对象在组合创建同时创建,销毁之前销毁。而聚合都是单个存在的,对象销毁,聚合的对象不一定。
了解设计模式的三个原则:单一职责原则、开放封闭原则、依赖倒转原则。
依赖倒转:高层模块不应该依赖底层模块,两个都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。里氏代换原则是子类型必须可以替换父类。
设计模式主要有创建型模式、结构型模式、行为型模式。
现在我们主要介绍比较常用的的十个模式,二十三种老是忘记。
单例模式
单例模式在一个项目中全局实例只有一个,通过这个实例向其他模板提供数据全局访问。任务队列
分为饿汉模式、懒汉模式。去直接的区别就是饿汉模式在类初始化时直接进行实例化,而懒汉模式在需要的适合再去实例化。简单来说,饿汉就是怕没有嘛,所以它提前实例化;懒汉懒得一,用我我在去实例化。
对于线程安全问题:饿汉没有线程安全问题,而要解决的是懒汉模式的线程安全问题,最直接的办法使用互斥锁。防止多个线程抢占实例化。双重检查锁定(嵌套两个if)
也可以使用原子变量atomic:store来存储单例对象,load加载单例对象。
工厂模式
- 一个基类,包含虚工厂函数,用于实现子类
- 多个子类,重写父类工厂函数,每个子类负责生成一种商品,相当于再次解耦,将工厂类职责再次拆分、细化。
抽象工厂模式
提供一个抽象的基类,然后在它子类中完成具体细节,每个子类对应一个生产线。
关于简单工厂、工厂模式和抽象工厂的区别
- 简单工场模式不能遵循开放封闭原则,工厂模式、抽象工厂模式可以
- 简单工厂模式只有一个工厂类,工厂和抽象工厂有多个工厂类
- 工厂模式创建的产品对象相对简单,抽象工厂模式创建的产品对象相对复杂
- 工厂模式创建的对象不需要提供抽象类(无可变因素)
- 抽象工厂创建的对象对应的类有抽象的基类(有可变因素)
我们简述一下抽象类:不能实例化;包含至少一个纯虚函数。总之只要有纯虚函数就是抽象类,不能实例化
观察者模式
在设计模式中也有一种类似于描述行为的模式,叫做观察者模式。观察者模式允许我们定义一种订阅机制,可在对象事件发生时通知所有观察者对象,使它们能够自动更新。观察者模式还有另一种名字叫“发布-订阅”模式。
应用场景:当一个对象状态发生改变时,并且需要改变其他对象的时候;或者当应用中一些对象观察其他对象的时候使用观察者模式。
策略模式
策略模式需要我们定义一系列的算法,并且将每种算法都放入到独立的类中,在实际操作的时候使这些算法对象可以相互替换。
策略模式中的若干个策略对象相互之间是完全独立的, 它们不知道其他对象的存在。当我们想使用对象中各种不同的算法变体,并希望能够在运行的时候切换这些算法时,可以选择使用策略模式来处理这个问题。
装饰者模式
代理模式
设配器模式
模板方法
建造者模式
单例(全局唯一),工厂模式(对象创建)、观察者模式(时间通知),策略模式(算法封装)。
链接: https://subingwen.cn/design-patterns/strategy/#1-%E6%A9%A1%E8%83%B6%E4%BA%BA
多态、虚函数、虚继承
多态通过基类指针/引用调用子类方法;虚函数支持动态绑定;虚继承解决菱形继承。
多态:一个接口多个实现。允许使用父类指针来操作子类对象,并根据对象实际类型来调用相应方法。静态多态(编译时多态)【函数重载、运算符重载、模板】、动态多态(运行时多态)【虚函数】
虚函数:实现运行时多态的关键。当一个类包含虚函数时,编译器会为该类创建一个虚函数表,每个类的对象内部包含一个指向其类的虚函数表指针(vptr)。在运行时,通过对象vptr找到正确的虚函数表,进而调用正确的函数。
纯虚函数:在基类中只有声明没有定义的虚函数。普通虚函数子类可以不重写,纯虚函数必须重写(不然无法实例化,也是抽象类)。
为什么析构函数是虚函数?将基类的析构函数声明为虚函数是为了确保当通过基类指针删除派生类对象时,能够正确调用派生类的析构函数,从而避免资源泄漏。
虚继承:解决菱形继承导致的数据冗余与二义性。
构造/析构拷贝构造/赋值运算符重载
构造初始化对象;析构释放资源;拷贝构造复杂对象;赋值运算符重载处理已有对象赋值。
1094

被折叠的 条评论
为什么被折叠?



