编辑
2023-08-03
code
00
请注意,本文编写于 413 天前,最后修改于 413 天前,其中某些信息可能已经过时。

目录

零碎知识小记
virtual
虚析构
构造函数调用虚函数
虚表
RAII
含义
应用
重载 隐藏 重写(覆盖)
互斥量mutex
非定时的互斥体类
定时的互斥体类
仿函数(functor)
Qt ConnectionType

零碎知识小记

virtual

虚析构

父指针指向的子对象执行delete,析构的时候,子对象的析构函数不会执行

cpp
class A { public: ~A(){ std::cout << __func__ << std::endl; } }; class B : public A { public: ~B(){ std::cout << __func__ << std::endl; } }; A* b = new B; delete b; // output '~A' { B c; } // output '~B ~A'

构造函数调用虚函数

基类可以调用虚函数,但调用的是基类的虚函数。子类还没有构造,不会触发多态

cpp
class A { public: A() { echo(); // A } virtual void echo() { std::cout << "A" << std::endl; } }; class B : public A { public: virtual void echo() { std::cout << "B" << std::endl; } }; B b; // output 'A'

虚表

  • 虚指针(vptr): 每个含有虚函数的对象里都有虚表指针,指向虚表
  • 虚函数表(vtable): 顺序存放虚函数地址
  • 虚指针存在于对象实例的最前面的位置,可以在多层继承多重继承时来保证最高性能
  • 虚函数表属于,类的所有对象通过指向虚函数表的虚指针vptr来共享虚函数表
  • 虚函数表vtable放在可执行文件的只读数据字段.rodata
  • 重写虚函数时,子类的函数会替换虚表中对应的函数

编译器会为每一个含有虚函数的类创建一个虚表,该虚表将被所有该类的所有对象共享,里面存储的是该类的虚函数的地址。虚表的大小是N*4(N个虚函数,一个虚函数占一行,最后以0结尾)。虚函数的实现就是通过虚表来实现的。之前讲到只有虚函数才能被覆盖,就是指的是虚表中虚函数地址被覆盖。 在有虚函数的类实例化时,编译器分配了指向该表的指针的内存(虚函数表指针vptr),简单一点就是便一起给每个对象添加了一个隐藏成员,这个隐藏成员中保存了一个指向虚函数表的指针。这意味着可以通过类实例化的地址得到虚表,然后遍历其中的函数指针,并调用相应的函数。

1,基类对象含有一个指针,该指针指向基类中所有虚函数的地址表。派生类对象将含有一个指向独立地址表的指针。 2,如果派生类提供了基类虚函数的重定义,该虚函数表将保存新函数的地址。即就是虚函数覆盖实际是对虚函数表中的虚函数的地址的覆盖。 3,如果派生类定义了新的虚函数,则该函数的地址将被加入到虚函数表中。注意,无论类中是一个还是多个虚函数,都只需在对象中添加一个地址成员,只是表的大小不同。

RAII

含义

  • 在构造函数中申请分配资源,在析构函数中释放资源。
  • 利用类来管理资源,将资源与类对象的生命周期绑定

应用

  • 在资源管理方面,智能指针(std::shared_ptrstd::unique_ptr)是RAII最具代表性的实现,使用了智能指针,可以实现自动的内存管理,再也不用担心忘记delete造成内存泄漏了。
  • 在状态管理方面,线程同步中使用std::unique_lockstd::lock_guard对互斥量std::mutex进行状态管理也是RAII的典型实现,通过这种方式,我们再也不用担心互斥量之间的代码出现异常而造成线程死锁。

重载 隐藏 重写(覆盖)

三者作用域有无virtual函数名形参列表返回值类型
重载相同可有可无相同不同可同可不同
隐藏不同可有可无相同可同可不同可同可不同
重写不同相同相同相同(协变)

互斥量mutex

非定时的互斥体类

  • std::mutex 已经拥有std::mutex所有权的线程不能在这个互斥体上再次调用 lock() 和 unlock(),否则可能导致死锁
  • std::recursive_mutex 行为与 std::mutex 类似,区别在于能在同一个互斥体上再次调用 lock() 和 unlock(),经常用于递归函数加锁

定时的互斥体类

  • std::timed_mutex

  • std::recursive_timed_mutex

  • std::shared_timed_mutex

    try_lock_for(rel_time) 在给定相对时间内获取锁,到点返回结果

    try_lock_util(abs_time) 获取锁直到给定的绝对时间

仿函数(functor)

通过重载operator ()运算符模拟函数形为的

image.png

Qt ConnectionType

  1. Qt::AutoConnection:如果接收者位于发出信号的线程中,则使用 Qt::DirectConnection。否则,使用 Qt::QueuedConnection。连接类型在信号发出时确定。

  2. Qt::DirectConnection:发出信号时立即调用槽函数。该槽函数在发出信号线程中执行。

  3. Qt::QueuedConnection:信号发出后,信号会暂时被放到一个消息队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,然后执行和信号关联的槽函数,这种方式既可以在同一线程内传递消息也可以跨线程操作。emit语句后的代码将在发出信号后立即被执行,无需等待槽函数执行完毕,槽函数在接收者所依附线程执行。

  4. Qt::BlockingQueuedConnection:槽函数的调用时机Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。而且接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。

  5. Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是为了避免重复连接

  6. Qt::SingleShotConnection:这是一个可以使用按位 OR 与上述任何一种连接类型组合的标志。当设置了 Qt::SingleShotConnection 时,槽只会被调用一次;发出信号时,连接将自动断开

本文作者:OhtoAi

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!