![](https://img-blog.csdnimg.cn/20190918135101160.png?x-oss-process=image/resize,m_fixed,h_224,w_224)
C++进阶剖析(shi lei)
设计模式、STL、智能指针等面试重点
干锅土鸡
这个作者很懒,什么都没留下…
展开
-
43. 设计模式2
结构性模式:关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。代理模式Proxy:/*代理Proxy模式 : 通过代理类,来控制实际对象的访问权限客户 助理Proxy 老板 委托类*/class VideoSite // #1 抽象类{public: virtual void freeMovie() = 0; // 免费电影 virtual void vipMovie() = 0; // vip电影 virtual void ticketMov原创 2022-03-03 14:43:14 · 357 阅读 · 0 评论 -
42.设计模式1
饿汉单例模式:(是线程安全的)一个类不管创建多少次对象,永远只能得到该类型一个对象的实例!class Singleton{public: static Singleton* getInstance()//获取类的唯一实例对象接口方法 { return &instance; }private: static Singleton instance;//定义一个唯一的类的实例对象 Singleton() { } Singleton(const Singleton&原创 2022-03-03 12:30:51 · 316 阅读 · 0 评论 -
41. 线程间同步通信-生产者消费者模型
std::mutex mtx;std::condition_variable cv;//条件变量,线程间的同步通信操作//生产者生产一个物品,通知消费者消费一个;消费完了,消费者再通知生产者生产!class Queue{public: void put(int val) { while(!que.empty()) { unique_lock<std::mutex> lck(mtx); //que不空,通知消费者消费,消费完了再继续生产 //下面两行代码实原创 2022-03-02 19:45:36 · 100 阅读 · 0 评论 -
40.C++11多线程
语言级别的多线程=》代码跨平台 Windows、linux、macosthread/mutex/condition_variableatomic原子类型,基于CAS操作的原子类型(线程安全)sleep_for本质上都是在调用系统的API一、如何创建启动线程?std::thread定义一个线程对象,传入线程所需要的线程函数和参数,线程自动开启!void threadHandler(int time){ std::this_thread::sleep_for(std::chrono::sec原创 2022-03-02 15:58:37 · 254 阅读 · 0 评论 -
39. C++11常用知识点总结
auto可以根据右值,推导出右值的类型,然后左边变量的类型也就已知了!nullptr指针专用(和整数进行区别)#define NULL 0(NULL其实是个宏定义)foreachfor(Type val : container) => 底层就是指针或者迭代器实现的右值引用:少了内存开辟,拷贝构造。move移动语义构造和forward类型完美转发函数模板新特性:typename… A 表示可变参(类型参数)函数对象function:函数对象bind绑定器 bind1st和bin.原创 2022-03-01 21:44:14 · 396 阅读 · 0 评论 -
38. lambda表达式实现原理、实践
函数对象的缺点使用在泛型算法参数传递,比较性质/自定义操作,优先级队列,智能指针删除器lambda表达式的语法:[捕获变量](形参列表)->返回值{操作代码}template<typename T=void>class TestLambda01{public: TestLambda01(){} void operator()()const { cout << "hello world!" << endl; }};template<原创 2022-03-01 20:59:53 · 365 阅读 · 0 评论 -
37.bind和function实现mini线程池
bind绑定器返回的结果还是一个函数对象bind是函数模板,可以自动推演模板类型参数class ThreadPool{public: ThreadPool(){} ~ThreadPool(){} void startPool(int size) { for(int i=0;i<size;++i) { _pool.push_back(new Thread(bind(&Thread::runInThread,this,i))); } }private: v原创 2022-03-01 12:25:11 · 88 阅读 · 0 评论 -
36. function实现原理
void hello(string str){ cout << str << endl;}int sum(int a,int b){ return a+b;}template<typename FTy>class myfunction{};template<typename R,typename A1>class myfunction<R(A1)>{public: using PFUNC = R(*)(A1);原创 2022-02-28 17:14:16 · 167 阅读 · 0 评论 -
35. function函数对象类型的应用实例、模板完全/部分特例化
function:绑定器,函数对象,lambda表达式本质上都是函数对象,只能使用在一条语句中!function作用?原创 2022-02-28 15:54:00 · 127 阅读 · 0 评论 -
34.绑定器、bind1st、bind2nd、my_find_if实现
C++ STL中的绑定器bind1st:operator()的第一个形参变量绑定成一个确定的值bind2nd:operator()的第二个形参变量绑定成一个确定的值int main(){ vector<int> vec; srand(time(nullptr)); for(int i=0;i<20;++i) { vec.push_back(rand()%100+1); } sort(vec.begin(),vec.end());//默认从小到大排序 retur.原创 2022-02-28 13:32:19 · 315 阅读 · 0 评论 -
33. 自定义删除器
智能指针能够保证资源绝对的释放!template<typename T>class MyDeletor{public: void operator()(T* ptr)const { delete[] ptr; }};template<typename T>class MyFileDeletor{public: void operator()(T* ptr)const { fclose(ptr); }};int main(){ unique原创 2022-02-28 09:58:22 · 98 阅读 · 0 评论 -
32.shared_ptr交叉引用问题
shared_ptr:强智能指针 可以改变资源的引用计数weak_ptr:弱智能指针 不会改变资源的引用计数交叉引用:造成new出来的资源无法释放,资源泄露问题!定义对象的时候,用强智能指针!引用对象的地方使用弱智能指针!改进:就可以了,weak_ptr不会+1;他本身没有提供* ->的重载,只会观察资源,不能访问资源!那他有什么用???:把他提升成强智能指针!多线程共享对象的线程安全问题!:...原创 2022-02-27 21:39:21 · 289 阅读 · 0 评论 -
31. 智能指针
template<typename T>class CSmartPtr{public: CSmartPtr(T* ptr = nullptr) :mptr(ptr) {} CSmartPtr(const CSmartPtr<T>& src) { mptr = new T(*src.mptr); } ~CSmartPtr() { delete mptr; mptr = nullptr; } T& operator*() {原创 2022-02-27 20:17:29 · 390 阅读 · 0 评论 -
30. 右值引用、move、forward
/*CMyString(const char*)CMyString(const char*)CMyString(const char*)CMyString(const CMyString&) => tmpStr拷贝构造main函数栈帧上的临时对象~CMyStringoperator=(const CMyString&) => main函数栈帧上的临时对象给t2赋值~CMyStringaaaaaaaaaaaaaaaaaaaa~CMyString~CMyStr..原创 2022-02-27 15:21:31 · 219 阅读 · 0 评论 -
29. 高效编程
是原创 2022-02-27 12:55:52 · 143 阅读 · 0 评论 -
28.面经(一)
多个进程用户空间是单独的隔离的,而内核空间是共享的!程序的内存布局堆栈函数调用参数如何传递(汇编层面)函数调用参数为什么从右向左压栈?(C、C++支持可变参函数,所以要从右向左压栈)内存的最小单位为1...原创 2022-02-26 20:29:55 · 289 阅读 · 0 评论 -
27.泛型算法和绑定器
#include <iostream>#include <vector>#include <algorithm> // 包含了C++ STL里面的泛型算法#include <functional> // 包含了函数对象和绑定器using namespace std;#if 0/*五、泛型算法 = template + 迭代器 + 函数对象特点一:泛型算法的参数接收的都是迭代器特点二:泛型算法的参数还可以接收函数对象(C函数指针)sort原创 2022-02-26 15:25:33 · 89 阅读 · 0 评论 -
26. 迭代器iterator、函数对象
#include <iostream>#include <vector>using namespace std;/*容器的迭代器const_iterator:常量的正向迭代器 只能读,而不能写了iterator:普通的正向迭代器reverse_iterator:普通的反向迭代器const_reverse_iterator:常量的反向迭代器*/#if 0int main(){ vector<int> vec; for (int i = 0;.原创 2022-02-26 14:33:43 · 284 阅读 · 0 评论 -
25. 无序关联容器、有序关联容器
// 不会存储key值重复的元素(插入50个,可能元素个数只有18,好多重复) unordered_set<int> set1; for (int i = 0; i < 50; ++i) { set1.insert(rand()%20+1); // vector/deque/list insert(it, val) } //哈希表元素的个数! //cout << set1.size() << endl; //值为15的元素的个数! //...原创 2022-02-26 13:20:41 · 354 阅读 · 0 评论 -
24. 详解容器适配器
标准容器-容器适配器如何理解适配器?适配器底层没有自己的数据结构,他是一个容器的封装,他的方法全部由底层依赖的容器进行实现的!没有实现自己的迭代器!template<typename T,typename Container=deque<T>>class Stack{public: void push(const T&val) { con.push_back(val); } void pop() { con.pop_back(); }原创 2022-02-26 00:28:29 · 109 阅读 · 0 评论 -
23. STL::deque、vector、list对比
deque:双端队列容器!底层是2倍扩容的动态数组!动态开辟的二维数组,一维数组从2开始,以二倍的方式扩容!每次扩容后,原来的第二维从新的第一维数组oldsize/2开始存放,上下都预留空行,方便支持deque的首位元素添加!deque<int> deq;deq.push_back(20);从末尾添加元素O(1)deq.push_front(20);从首部添加元素O(1)vector从首部添加:vec.insert(vec.begin(),20) O(n)deq.insert原创 2022-02-25 23:32:44 · 367 阅读 · 0 评论 -
22.STL::vector
vector:向量容器底层数据结构:动态开辟的数组,每次以原来大小的2倍进行扩容!vector<int> vec增加:vec.push_back(20);时间复杂度O(1) 可能导致容器扩容。容器中进行对象的构造析构,内存的开辟释放,都是通过allocator空间适配器实现的!allocate deallocate construct construct四个函数!vec.insert()时间复杂度O(n)删除:vec.pop_back()末尾删除元素O(1)vec.e原创 2022-02-25 00:05:17 · 349 阅读 · 0 评论 -
21.四种类型转换方式
C++语言级别的四种类型转换方式:const_cast:去掉常量属性的一个类型转换!<>里面必须是个指针或者引用!!!汇编层面是一样子的!static_cast:提供编译器认为安全的类型转换!没有任何联系的类型之间的转换就会被否定!!reinterpret_cast:类似于C风格的强制类型转换!dynamic_cast:主要用于继承结构中,可以支持RTTI类型识别的上下转换!class Base{public: virtual void func() = 0;原创 2022-02-24 20:42:07 · 195 阅读 · 0 评论 -
20.菱形继承
虚继承可以解决菱形继承带来的数据重复、浪费资源的情况!C++多重继承-菱形继承的问题:派生类有多个间接基类的数据!如何解决?虚继承!原创 2022-02-24 19:38:45 · 65 阅读 · 0 评论 -
19.理解虚基类虚继承
抽象类:有纯虚函数的类virtual:修饰成员方法是虚函数可以修饰继承方式,是虚继承。被虚继承的类,叫做虚基类!如果不是虚继承,那么内存分配:A::mamb如果虚继承的话:vbptrmbA::ma0:向上的偏移量!8:到虚基类数据的偏移量!class A{public: virtual void func() { cout << "call A::func" << endl; } void operator delete(void *ptr)原创 2022-02-24 19:10:19 · 277 阅读 · 0 评论 -
18.继承多态笔试题实战分析
class Animal{public: Animal(string name) :_name(name) {} // 纯虚函数 virtual void bark() = 0;protected: string _name;};// 以下是动物实体类class Cat : public Animal{public: Cat(string name) :Animal(name) {} void bark() { cout << _name << " bar原创 2022-02-24 15:08:33 · 221 阅读 · 0 评论 -
17. 虚析构函数、再谈动态绑定、多态到底是啥、抽象类
虚函数能产生函数地址!存储在虚函数表对象必须存在,因为只有对象,才存在虚函数指针、表、虚函数地址构造函数完成,对象才产生!构造函数不能是virtual,构造函数调用虚函数,不会发生静态绑定!(构造函数中,调用的任何函数都是静态绑定的!)派生类对象构造过程:先调用父类构造函数,再调用子类构造函数!virtual+static是不行的,因为静态方法不依赖对象!(联想到第二点!)//如果父类析构函数不是虚函数:Base *pb = new Derive(10);pb->show(); /.原创 2022-02-24 13:24:21 · 213 阅读 · 0 评论 -
16. 虚函数、静态绑定、动态绑定
改一波(改成虚函数):总结一:如果类里面定义了虚函数,那么在编译阶段,编译器给这个类类型产生一个唯一的vftable虚函数表,虚函数表中主要存储的内容就是==RTTI指针和虚函数的地址!==当程序运行时每一张虚函数表都会加载到内存的.rodata区!(只读数据区)RTTI:run-time type information 运行时的类型信息。此时虚函数表是Base,所以Base vftable的RTTI指针指向代表“Base”这个字符串!上述代码加了虚函数关键字之后,sizeof大小变成了.原创 2022-02-23 22:26:30 · 263 阅读 · 0 评论 -
15. 继承、重载、覆盖、隐藏
继承的本质和原理本质:a.代码复用 b.子类父类相同的变量名字是没有关系的!派生类怎么初始化从基类继承来的成员变量呢?解答:通过调用基类相应的构造函数来初始化,不允许用派生类初始化列表的方法初始化基类成员变量(报错!),只能像这样子(调用构造函数):派生类的构造函数和析构函数,负责初始化和清理派生类部分派生类从基类继承来的成员,的初始化和清理由谁负责呢?是由基类的构造和析构函数来负责派生类优先在自己作用域查找函数,没有才去基类找!上面的代码:d.show()没有问题,但是d.s.原创 2022-02-23 19:02:32 · 171 阅读 · 0 评论 -
14. 对象池、new、new[ ]、delete、delete[ ]混用
new和deletemalloc和new区别malloc按字节开辟内存;new开辟内存时需要指定类型new int[10]所以malloc开辟内存返回的都是void* ,上面的operator new返回的是int*malloc只负责开辟空间,new不仅仅有malloc的功能,可以进行数据的初始化new int(20);new int20;(20个元素初始化为0)malloc开辟内存失败返回nullptr指针;new抛出来的是bad_alloc类型的异常free和delete区别?de.原创 2022-02-22 23:43:21 · 191 阅读 · 0 评论 -
13. 迭代器失效问题、迭代器失效底层原理及实现
上述程序很大可能报错!这就叫迭代器失效!(扩容也失效!)当容器调用erase方法后,当前位置到容器末尾元素所有迭代器全部失效当容器调用insert方法后,当前位置到容器末尾元素所有迭代器全部失效迭代器失效后,如何解决?对插入删除点的迭代器进行更新操作!:erase后返回erase元素的下一个元素的迭代器!insert后返回的是插入的元素的迭代器迭代器失效如何实现?#include <iostream>using namespace std;#if 0// 定义容器..原创 2022-02-22 15:10:52 · 445 阅读 · 0 评论 -
13. vector容器迭代器iterator自实现
以下是上一篇博客实现的带空间适配器alloctor的vector代码!template<typename T>struct Allocator{ //负责内存开辟 T* allocate(size_t size) { return (T*)malloc(sizeof(T)*size); } //负责内存释放! void deallocate(void* p) { free(p); } //负责对象构造! void construct(T* p,const T&a原创 2022-02-22 13:02:49 · 754 阅读 · 0 评论 -
12.复数类、重载<< >>、string类、iterator迭代器
class CComplex{public: CCOmplex(int r=0,int i=0) :mreal(r),mimage(i) {} //指导编译器如何做CComplex类对象的加法操作 CComplex operator+ (const CComplex& com) { CComplex comp; comp.mreal = this->mreal + src.mreal; comp.mimage = this->mimage + src.mim原创 2022-02-22 12:12:37 · 465 阅读 · 0 评论 -
11. 函数模板、类模板、实现vector顺序容器、空间适配器
// 模板的声明template<typename T> bool compare(T a, T b){ return a>b;}int main(){ compare<int>(20,30); //模板的实参推演! compare(20,30);}编译器优先把函数名看成普通函数,找不到的话,才考虑是个函数模板!:compare (“aaa”,“bbb”);模板不能分开文件定义(一个文件定义,一个文件使用)!因为编译产生符号,链接时找不到符号,出原创 2022-02-21 13:48:51 · 599 阅读 · 0 评论 -
10. 静态成员变量、静态成员方法、指向类成员的指针
静态成员变量初始化只能在类外初始化静态成员变量,在类大小计算时,不计算大小!(不考虑)不属于对象,而是属于类级别(全局对象)static 静态成员方法和普通成员方法区别:静态方法调用使用作用域调用(类名),他不需要依赖对象,所以不产生this指针!它不需要接受对象的地址而普通方法会产生this指针!所以必须用对象来调用!类的静态成员方法不能调用类的普通成员对象,因为它不会生成this指针!但是可以调用静态成员变量(static)!const 常对象不能调用非const成员函数!为..原创 2022-02-20 22:31:40 · 266 阅读 · 0 评论 -
9.类和对象实践、初始化列表
class String{public: String(const char* str = nullptr) { if(str!=nullptr) { m_data = new char[strlen(str) + 1]; strcpy(this->m_data,str); } else { m_data = new char[1]; *m_data = '\0'; } } String(const String& other) {原创 2022-02-20 18:51:17 · 250 阅读 · 0 评论 -
8. 析构、构造、深拷贝、浅拷贝、赋值操作符重载
OOP实现一个顺序栈class SeqStack{public: //构造函数 //可以带参数,所以可以重载,有多个构造函数 SeqStack(int size = 10) { _pstack = new int[size]; _top = -1; _size = size; } //析构函数 ~SeqStack() { delete[] _pstack; _pstack = nullptr; } void push(int val) { if(full(原创 2022-02-20 11:47:06 · 81 阅读 · 0 评论 -
7.new、delete、OOP、this指针
malloc和free成为C的库函数new和delete称作运算符malloc开辟内存失败,是通过返回值与nullptr作比较;而new开辟内存失败,是通过抛出bad_alloc类型的异常来判断!int* q1 = new int[20];int* q1 = new int20;二者区别在于,第一个只负责开辟不初始化,第二个会初始化为0new有多少种?int* p1 = new int[20];int* p2 = new (nothrow) int;//不抛异常const int* p3原创 2022-02-19 21:08:29 · 318 阅读 · 0 评论 -
7. C++左值引用和右值引用
引用是一种更安全的指针!引用必须初始化,指针可以不用初始化!打印20 4 20int &&a = 30;const int& = 30;在底层汇编是一样子的!一个右值引用变量本身是个左值!原创 2022-02-19 19:31:38 · 85 阅读 · 0 评论 -
6. const用法、一二级指针结合
C文件:打印出来都是30,const修饰的量叫做常变量!不能作为数组参数!C++文件:C++中 ,所有出现const常量名字的地方,都被常量的初始化替换!(参考狄泰C++剖析专栏,涉及到常量表的概念!)在编译阶段设计到常量a的就被替换成常量了!const和一二级指针结合sonst修饰的量常出现的错误是:常量不能再作为左值不能把常量的地址泄露给一个普通的指针或者普通的引用变量(可以间接修改常量的值!)C++语言规范:const修饰的是离他最近的类型!第一行:*p不能在作为左值:*.原创 2022-02-19 15:38:05 · 297 阅读 · 0 评论