GetMessage和PeekMessage
PeekMessage
在处理获得消息时候和GetMessage
一样,关键不同的是PeekMessage
在没有消息处理的时候还会继续保持循环激活状态,并且继续占用资源。
GetMessage
每次都会等待消息,直到取到消息才会返回。PeekMessage
只是查询消息队列,没有消息就立即返回,从返回值判断是否取到了消息。GetMessage
从消息队列中取不到消息,则线程就会被操作系统挂起,等待 OS 重新调度该线程;而PeekMessage
线程会得到 CPU 的控制权,运行一段时间。GetMessage
是从消息队列中“取出”消息,就把消息从消息队列中删除;PeekMessage
的主要功能是“窥视”消息,如果有消息,就返回true
,否则返回false
。另外,也可以使用PeekMessage
从消息队列中取出消息,这个功能涉及到它的一个参数(UINT wRemoveMsg
),如果设置为PM_REMOVE
,消息则被取出并从消息队列中删除;如果设置为PM_NOREMOVE
,消息就不会从消息队列中取出。
vector和list
vector
是一段连续的内存空间,是物理上的连续;而list
底层是双向链表实现的,它的节点之间是通过指针来体现出逻辑上的连续,在内存分配上,不是连续的空间。vector
由于是连续的内存,所以它支持随机访问,时间复杂度为O(1)
;而list
则需要通过指针来遍历,时间复杂度是O(n)
。vector
是连续的空间,所以它插入删除元素,就可能需要元素移动拷贝等操作;list
插入删除不涉及数据的激动,改变相关节点的后继或前驱指针的值即可。- 添加元素时,
vector
由于涉及空间的重新分配,所以指向容器的迭代器、指针以及引用会全部失效;而list
则全都不失效。 - 删除元素时,
vector
是被删除元素之前的迭代器是有效;list
删除元素的时候也是全都不失效。
多继承有几个虚函数表
单独基类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
typedef void(*Fun)(void); class Base { public: virtual void f() { std::cout << "Base::f()" << std::endl; } virtual void g() { std::cout << "Base::g()" << std::endl; } virtual void h() { std::cout << "Base::h()" << std::endl; } }; int main() { Base b; Fun pf = nullptr; std::cout << "虚函数表的地址:" << &b << std::endl; std::cout << "虚函数表里面第一个函数的地址:" << (int*)*(int*)(&b) << std::endl; pf = (Fun)*((int*)*(int*)(&b)); //f() pf(); //g() ((Fun)*((int*)*(int*)(&b)+1))(); //h() ((Fun)*((int*)*(int*)(&b)+2))(); return 0; } |
一张虚函数
继承基类但没有覆盖基类虚函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
typedef void(*Fun)(void); class Base { public: virtual void f() { std::cout << "Base::f()" << std::endl; } virtual void g() { std::cout << "Base::g()" << std::endl; } virtual void h() { std::cout << "Base::h()" << std::endl; } }; class Derive : public Base { public: virtual void f1() { std::cout << "Derive::f1()" << std::endl; } virtual void g1() { std::cout << "Derive::g1()" << std::endl; } virtual void h1() { std::cout << "Derive::h1()" << std::endl; } }; int main() { Derive d; Fun pf = nullptr; std::cout << "虚函数表的地址:" << &d << std::endl; std::cout << "虚函数表里面第一个函数的地址:" << (int*)*(int*)(&d) << std::endl; pf = (Fun)*((int*)*(int*)(&d)); //f() pf(); //g() ((Fun)*((int*)*(int*)(&d)+1))(); //h() ((Fun)*((int*)*(int*)(&d)+2))(); //f1() ((Fun)*((int*)*(int*)(&d)+3))(); //g1() ((Fun)*((int*)*(int*)(&d)+4))(); //h1() ((Fun)*((int*)*(int*)(&d)+5))(); return 0; } |
一张虚函数表
继承基类并覆盖了基类虚函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
typedef void(*Fun)(void); class Base { public: virtual void f() { std::cout << "Base::f()" << std::endl; } virtual void g() { std::cout << "Base::g()" << std::endl; } virtual void h() { std::cout << "Base::h()" << std::endl; } }; class Derive : public Base { public: virtual void f() { std::cout << "Derive::f()" << std::endl; } virtual void g1() { std::cout << "Derive::g1()" << std::endl; } virtual void h1() { std::cout << "Derive::h1()" << std::endl; } }; int main() { Base*b = new Derive(); b->f(); Fun pf = nullptr; std::cout << "虚函数表的地址:" << b << std::endl; std::cout << "虚函数表里面第一个函数的地址:" << (int*)*(int*)b << std::endl; pf = (Fun) * ((int*)*(int*)(b)); //f() pf(); //g() ((Fun) * ((int*)*(int*)(b) + 1))(); //h() ((Fun) * ((int*)*(int*)(b) + 2))(); //f1() ((Fun) * ((int*)*(int*)(b) + 3))(); //g1() ((Fun) * ((int*)*(int*)(b) + 4))(); //h1() return 0; } |
这时还是一张虚函数表。不过
f()
函数被覆盖了,所以派生类的虚函数表里面只有5个函数。
继承多个基类但没有覆盖基类虚函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
typedef void(*Fun)(void); class Base1 { public: virtual void f() { std::cout << "Base1::f()" << std::endl; } virtual void g() { std::cout << "Base1::g()" << std::endl; } virtual void h() { std::cout << "Base1::h()" << std::endl; } }; class Base2 { public: virtual void f() { std::cout << "Base2::f()" << std::endl; } virtual void g() { std::cout << "Base2::g()" << std::endl; } virtual void h() { std::cout << "Base2::h()" << std::endl; } }; class Base3 { public: virtual void f() { std::cout << "Base3::f()" << std::endl; } virtual void g() { std::cout << "Base3::g()" << std::endl; } virtual void h() { std::cout << "Base3::h()" << std::endl; } }; class Derive : public Base1,public Base2,public Base3 { public: void f1() { std::cout << "Derive::f1()" << std::endl; } void g1() { std::cout << "Derive::g1()" << std::endl; } void h1() { std::cout << "Derive::h1()" << std::endl; } }; int main() { Derive d; Fun pf = nullptr; std::cout << "虚函数表一的地址:" << (int*)(&d) << std::endl; std::cout << "虚函数表二的地址:" << (int*)(&d)+1 << std::endl; std::cout << "虚函数表三的地址:" << (int*)(&d)+2 << std::endl; std::cout << "虚函数表一里面第一个函数的地址:" << (int*)*((int*)(&d)+0) << std::endl; std::cout << "虚函数表二里面第一个函数的地址:" << (int*)*((int*)(&d)+1) << std::endl; std::cout << "虚函数表三里面第一个函数的地址:" << (int*)*((int*)(&d)+2) << std::endl; //Base1::f() ((Fun)*((int*)*((int*)(&d)+0)+0))(); //Base1::g() ((Fun)*((int*)*((int*)(&d)+0)+1))(); //Base1::h() ((Fun)*((int*)*((int*)(&d)+0)+2))(); //Base2::f() ((Fun)*((int*)*((int*)(&d)+1)+0))(); //Base2::g() ((Fun)*((int*)*((int*)(&d)+1)+1))(); //Base2::h() ((Fun)*((int*)*((int*)(&d)+1)+2))(); //Base3::f() ((Fun)*((int*)*((int*)(&d)+2)+0))(); //Base3::g() ((Fun)*((int*)*((int*)(&d)+2)+1))(); //Base3::h() ((Fun)*((int*)*((int*)(&d)+2)+2))(); return 0; } |
3张虚函数表
继承多个基类并覆盖了基类虚函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
typedef void(*Fun)(void); class Base1 { public: virtual void f() { std::cout << "Base1::f()" << std::endl; } virtual void g() { std::cout << "Base1::g()" << std::endl; } virtual void h() { std::cout << "Base1::h()" << std::endl; } }; class Base2 { public: virtual void f() { std::cout << "Base2::f()" << std::endl; } virtual void g() { std::cout << "Base2::g()" << std::endl; } virtual void h() { std::cout << "Base2::h()" << std::endl; } }; class Base3 { public: virtual void f() { std::cout << "Base3::f()" << std::endl; } virtual void g() { std::cout << "Base3::g()" << std::endl; } virtual void h() { std::cout << "Base3::h()" << std::endl; } }; class Derive : public Base1,public Base2,public Base3 { public: void f() { std::cout << "Derive::f()" << std::endl; } void g1() { std::cout << "Derive::g1()" << std::endl; } void h1() { std::cout << "Derive::h1()" << std::endl; } }; int main() { Derive d; Base1* b1 = &d; Base2* b2 = &d; Base3* b3 = &d; b1->f(); b2->f(); b3->f(); b1->g(); b2->g(); b3->g(); Fun pf = nullptr; std::cout << "虚函数表一的地址:" << (int*)(&d) << std::endl; std::cout << "虚函数表二的地址:" << (int*)(&d)+1 << std::endl; std::cout << "虚函数表三的地址:" << (int*)(&d)+2 << std::endl; std::cout << "虚函数表一里面第一个函数的地址:" << (int*)*((int*)(&d)+0) << std::endl; std::cout << "虚函数表二里面第一个函数的地址:" << (int*)*((int*)(&d)+1) << std::endl; std::cout << "虚函数表三里面第一个函数的地址:" << (int*)*((int*)(&d)+2) << std::endl; //Base1::f() ((Fun)*((int*)*((int*)(&d)+0)+0))(); //Base1::g() ((Fun)*((int*)*((int*)(&d)+0)+1))(); //Base1::h() ((Fun)*((int*)*((int*)(&d)+0)+2))(); //Base2::f() ((Fun)*((int*)*((int*)(&d)+1)+0))(); //Base2::g() ((Fun)*((int*)*((int*)(&d)+1)+1))(); //Base2::h() ((Fun)*((int*)*((int*)(&d)+1)+2))(); //Base3::f() ((Fun)*((int*)*((int*)(&d)+2)+0))(); //Base3::g() ((Fun)*((int*)*((int*)(&d)+2)+1))(); //Base3::h() ((Fun)*((int*)*((int*)(&d)+2)+2))(); return 0; } |
3张虚函数表,派生类中的
f()
函数覆盖了基类的f()
函数,表现出了多态性。
安全问题
- 通过基类指针访问派生类独有的虚函数,将编译出错。
- 可以通过指针偏移的方式,调用基类中
private
或protected
的虚函数。
vector和list使用场景
vector
- 连续空间,支持随机访问,查找效率高,时间复杂度
O(1)
- 插入删除可能需要对数据进行移动,所以比较麻烦,时间复杂度
O(n)
,另外当空间不足时还需要进行扩容。 - 适用于偏向随机访问,不在乎插入删除的效率的情况。
list
- 底层实现是循环双链表,当对数据进行插入删除时,其时间复杂度
O(1)
- 不是连续的空间,只能通过指针来访问,所以查找数据需要遍历,其时间复杂度
O(n)
,没有提供[]
操作符的重载 - 适用于高效的插入删除,不关心随机访问的情况。
多线程安全问题
什么时候会出现
当多个线程同时访问同一个资源时,可能会涉及。(资源包括一个变量、一个对象、一个文件、一个数据库表等)
解决
同步互斥访问。在同一时刻,只能有一个线程访问该资源。
互斥量
是一种锁机制。可以让每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束后解锁。
死锁
产生死锁的必要条件
- 互斥条件
- 在一段时间内某资源仅为一进程所占用
- 请求和保持条件
- 当进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件
- 进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放
- 环路等待条件
- 在发生死锁时,必然存在一个进程--资源的环形链
出现的原因
- 竞争资源
- 进程间推进顺序非法
解决死锁的方法
- 预防死锁
- 以确定的顺序获得锁
- 超时放弃
- 避免死锁
- 银行家算法
- 检测死锁
- 首先为每个进程和每个资源指定一个唯一的号码
- 然后建立资源分配表和进程等待表
- 解除死锁
- 剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态
- 撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态消除为止。
map和set
底层数据结构是红黑树。
map
是以键值的方式存储的。键是pair
类型的 first,值是pair
类型的 second.
set
存储的只有一个 key。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//是否为空 empty //元素个数 size //最大容纳数 max_size //查找元素,找到的话返回该元素的迭代器,找不到的话返回NULL find //计算集合中某一个特定元素的个数,因为集合中不会出现相同的元素 //所以该接口只能返回1或0,经常用来判断集合中一个元素在不在 count //第一个元素的迭代器,lower_bound返回5,upper_bound返回6。 vector<int> aet{5,6,7,8,9,0}; lower_bound upper_bound //返回一组范围的键值对 equal_range |
多态
C++的四大特性
- 抽象
- 封装
- 多态
- 继承
构造函数是否可为虚
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 2020_04_2905/01
- ♥ 2022_02_2602/26
- ♥ 2022_02_24_0103/01
- ♥ 2020_11_19_0102/16
- ♥ 2022_09_1409/26
- ♥ 后端知识点记述 一09/08