Qt知识随笔记

信号和槽的链接方式

1.自动连接(Qt::AutoConnection)

描述:这是Qt的默认连接方式。根据信号和槽所属的线程关系自动选择连接方式。
行为:如果信号和槽在同一个线程中,则使用直接连接。
如果信号和槽在不同线程中,则使用队列连接。
适用场景:通常是最常用的选择,适合大部分情况,因为它能根据情况自动选择合适的连接方式。

2. 直接连接(Qt::DirectConnection)

描述:信号发出后立即调用槽函数,槽函数在信号发出者的线程中执行。
行为:信号与槽的调用顺序与代码顺序一致,即同步调用。
适用场景:适用于信号和槽在同一个线程的情况,或希望确保信号发出后立即执行槽。注意:如果信号和槽在不同线程中使用这种连接方式,可能会引发并发问题。

3. 队列连接(Qt::QueuedConnection)

描述:信号发出后,槽函数的调用会被放入接收者线程的事件队列中,槽函数会在接收者线程空闲时执行。
行为:信号和槽的调用是异步的,即信号发出后立即返回,槽函数的执行由事件循环调度。
适用场景:适用于信号和槽在不同线程的情况,能确保信号和槽调用的线程安全性。也适用于希望异步执行槽函数的场景。

4. 阻塞队列连接(Qt::BlockingQueuedConnection)

描述:这是队列连接的特殊形式,信号发出后将槽函数调用放入接收者线程的事件队列中,等待槽函数执行完毕后再返回。
行为:信号发出方会被阻塞,直到槽函数在接收者线程中执行完毕。
适用场景:仅在信号和槽不在同一个线程中时可用,常用于需要线程同步的场景。使用此连接时需谨慎,因为如果信号和槽在同一个线程中使用这种方式,程序会死锁。

5. 唯一连接(Qt::UniqueConnection)

描述:确保相同的信号和槽的连接只会存在一次,防止重复连接。
行为:如果尝试重复连接同一个信号和槽,将不会建立新的连接。
适用场景:适用于防止重复连接引发的逻辑错误或资源浪费情况。例如,有些信号可能会在特定条件下多次连接同一槽,使用唯一连接可以避免多次执行相同的槽。

智能指针:

1. QSharedPointer

用途:基于引用计数的共享所有权指针,多个指针可以共享同一对象,当最后一个引用被销毁时,对象自动释放。
特点:
类似std::shared_ptr,但提供更好的Qt框架集成。
支持自定义删除器(如QSharedPointer::CustomDeleter)。
线程安全(引用计数操作是原子的)。
示例:

QSharedPointer ptr1(new MyClass); QSharedPointer ptr2 = ptr1; // 引用计数增加 ptr2.clear(); 
// 引用计数减少,若为0则对象被删除

注意事项:
避免循环引用,否则会导致内存泄漏(可用QWeakPointer解决)。
不支持管理数组,需用QScopedArrayPointer或手动指定删除器。

2. QWeakPointer

用途:配合QSharedPointer使用,提供对共享对象的弱引用,不会增加引用计数,避免循环引用。
特点:
类似std::weak_ptr,需通过toStrongRef()提升为QSharedPointer访问数据。
可检查对象是否已被销毁。
示例:

QSharedPointer shared(new MyClass); QWeakPointer weak = shared; if (!weak.isNull()) {
     QSharedPointer locked = weak.toStrongRef(); 
    // 安全使用locked 
}

3. QScopedPointer

用途:独占所有权的指针,对象在作用域结束时自动删除。
特点:类似std::unique_ptr,不可复制,但可通过移动转移所有权。
轻量级,无额外开销。
示例:

QScopedPointer scoped(new MyClass); 
scoped->doSomething();     // 离开作用域时自动删除对象 } 

操作:
reset():手动释放对象。
release():返回原始指针并放弃所有权。
注意事项:不适用于共享所有权场景。
支持自定义删除器(需通过模板参数指定)。

4. QScopedArrayPointer

用途:管理动态数组,析构时调用delete[]。
示例:

QScopedArrayPointer array(new int[100]); 
array[0] = 42; // 支持[]操作符 

5. QPointer

用途:弱引用QObject及其派生类,当对象被销毁时自动置为nullptr。
特点:不管理生命周期,仅观察对象是否存在。
适用于Qt对象(如窗口部件),避免访问已删除对象。
示例:

QObject* obj = new QObject;
 QPointer qptr = obj;
 delete obj;
 if (qptr.isNull())
 { 
    // 对象已被删除
 }

6. QSharedDataPointer

用途:实现隐式共享(写时复制),用于需要高效共享数据的场景(如Qt容器类)。
示例:

class SharedData : public QSharedData
 {
 public:
     int value;
 };
 class MyClass
 {
 public:
     QSharedDataPointer data;
 };
 MyClass a;
 a.data->value = 10;
 MyClass b = a; // 数据共享,不复制
 b.data->value = 20; // 写时复制,a和b数据分离

在Qt中使用智能指针时,需要注意以下关键事项,以确保代码的安全性和高效性:

1. 区分不同类型的智能指针

QSharedPointer基于引用计数,多个指针共享对象所有权。需注意循环引用问题,可能导致内存泄漏。配合QWeakPointer打破循环。
QScopedPointer作用域指针,离开作用域自动释放。不可复制,适合单一所有权场景。
QWeakPointer不持有对象所有权,需通过lock()转换为QSharedPointer才能访问对象。用于观察共享资源,避免循环引用。

2. 避免循环引用

当两个QSharedPointer相互引用时,引用计数无法归零,导致内存泄漏。 解决方案:将其中一个改为QWeakPointer,例如在父子对象或观察者模式中。

3. 与Qt对象树机制协同

Qt的传统父子对象机制会自动删除子对象。若对QObject使用智能指针:
避免同一对象同时由父对象树和智能指针管理,可能导致双重释放。
可使用QPointer跟踪QObject存活状态(类似QWeakPointer,但专为QObject设计)。

4. 线程安全性

QSharedPointer的引用计数操作是原子的,但对象本身的线程访问需手动同步(如加锁)。
QScopedPointer非线程安全,仅限单线程使用。

5. 自定义删除器

QSharedPointer支持运行时指定删除器,例如处理数组或特殊资源:

QSharedPointer ptr(new int[10], [](int *p)
{
 delete[] p;
}
); 

QScopedPointer删除器需通过模板参数指定,灵活性较低:

QScopedPointer> arr(new int[10]); 

6. 避免混合使用不同智能指针

不要将同一原始指针交给多个智能指针(如QSharedPointer和std::shared_ptr),可能导致重复释放。
Qt与STL智能指针的删除逻辑可能不兼容,需统一管理策略。

7. 传递智能指针的参数

优先按const引用传递:避免不必要的引用计数增减,提升性能。

void process(const QSharedPointer& ptr); 

需要传递所有权时,使用右值引用或std::move:

void takeOwnership(QSharedPointer&& ptr); 

8. 数组的处理

使用QSharedPointer<T[]>或自定义删除器(如delete[]):

QSharedPointer arr(new int[100]); 

9. 避免泄露原始指针

不要长期保存ptr.get()返回的裸指针,智能指针可能释放资源后,裸指针成为悬空指针。
在接口设计中优先使用智能指针,而非暴露原始指针。

10. 异常安全

智能指针在异常发生时能自动释放资源,但需确保在可能抛出异常的操作前完成资源获取。例如:

QSharedPointer res(new Resource); riskyOperation(); // 可能抛出异常,但res已安全持有资源 

总结示例

// // 示例:避免循环引用
class A : public QObject {
public:
    QWeakPointer<B> b_weak; // 使用QWeakPointer打破循环
};
class B : public QObject {
public:
    QSharedPointer<A> a_ptr;
};
// 示例:自定义删除器
QSharedPointer<FILE> filePtr(fopen("data.txt", "r"), [](FILE *f) {
    if (f) fclose(f);
});

// 示例:QObject与智能指针结合

QSharedPointer<QObject> obj = QSharedPointer<QObject>(new QObject);
QPointer<QObject> observer = obj.data(); // 安全观察对象状态 

通过遵循这些注意事项,可以充分发挥Qt智能指针的优势,同时规避潜在的内存管理和设计陷阱。
GDB/LLDB: 在调试器中暂停程序(如Ctrl+C),检查各线程的堆栈信息,查看哪些线程在等待锁。

bash复制代码

gdb -p <进程ID> (gdb) thread apply all bt # 查看所有线程的堆栈

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Qt笔记总结 作者:hackett 微信公众号:加班猿 一、常用控件 按钮类 QPushButton QtoolB...
    加班猿阅读 3,923评论 0 1
  • 简介 这里简单介绍Qt的一些核心机制,具体参见Qt文档。 主要包含内容: Qt的信号和槽,以及事件机制 Qt Ob...
    QuietHeart阅读 5,561评论 0 3
  • 1:new delete 与 malloc free的区别 1-> new是C++运算符,malloc是C的库...
    已二锅阅读 7,152评论 1 6
  • 在 Qt 开发中,性能优化是一个重要的环节,尤其是在处理复杂 UI、大量数据或高实时性要求的应用时。以下是一些常见...
    java_dev_bj阅读 1,937评论 0 0
  • Qt/QML 编码规范 一. 概述 良好的编码规范可以大幅提高程序的可读和可理解性,最终目标是实现更友好的可维护性...
    赵者也阅读 6,978评论 0 5