QT 智能指针 QPointer QScopedPointer QSharedPointer QWeakPointer QSharedDataPointer 隐式共享 显示共享

这篇具有很好参考价值的文章主要介绍了QT 智能指针 QPointer QScopedPointer QSharedPointer QWeakPointer QSharedDataPointer 隐式共享 显示共享。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

QPointer

QPointer 是一种受保护的指针,当其引用的对象被销毁时,它会被自动清除(但是,销毁引用对象还是必须手动delete)。QPointer所指向的对象必须是QObject或其派生类对象。

当多个指针指向同一个 Object 对象时,引用的对象可能被释放掉,这时使用 QPointer 就可以安全的测试引用对象是否有效,防止发生指针空悬。

注意:Qt5 之前,QPointer 指向一个 QWidget 对象(或子类对象)时,QPointer 由 QWidget 的析构函数清除,Qt5 之后 由 QObject 的析构函数清除。在析构函数销毁被跟踪 QWidget 的子项之前,任何跟踪 QWidget 的 QPointers 都不会被清除。

QPointer 提供的函数和运算符与普通指针的函数和运算符相同,但算术运算符+、-、 ++ 和 --除外(它们通常仅用于对象数组)。

创建 QPointer 指针,可以使用构造函数、用 T * 赋值或相同类型的其他 QPointer 。QPointer 比较可以使用 == 和 !=,或使用 isNull() 进行测试。可以使用 nullptr 或 *xx->member 取消引用。

QPointer 和普通指针可以混用,QPointer会自动转换为指针*。可以把 QPointer 对象传递给需要 QWidget * 参数的函数。因此,声明函数时没有必要用 QPointer 作为参数,只需使用普通指针即可。

#include <QCoreApplication>
#include <QTimer>
#include <QDebug>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QTimer *timer = new QTimer;
    // QPointer<QTimer> timer = new QTimer;
    delete timer;
    // 不使用QPointer时需要设置timer为NULL
    // timer=NULL;
    if(timer)
    {
        qInfo()<<"timer is not null";
    }
    else
    {
        qInfo()<<"timer is null";
    }
    return a.exec();
}

不使用QPointer时,输出“Label is not null”,原因是,delete后未置空,易造成指针悬空。使用 QPointer 时,输出"timer is null"。
在QPointer中 .是指针的属性,-> 是对象的属性。

QScopedPointer

QScopedPointer 类似于 C++ 11 中的 unique_ptr 用于管理动态分配的对象的独占所有权,即同一时间只能有一个QScopedPointer指向该对象。

QScopedPointer使用了RAII(资源获取即初始化)技术,当QScopedPointer被销毁时,它将自动释放所管理的对象的内存。QScopedPointer不支持拷贝和赋值操作,这是为了避免在多个指针之间共享所有权的问题。如果需要在多个指针之间转移所有权,应该使用QSharedPointer或QWeakPointer。

手动管理堆中分配的对象非常困难而且容易出错,容易导致内存泄露。QScopedPointer是一个简化内存管理的工具类,它通过将堆分配的空间赋值给基于栈的内存,通常称为RAII(resource acquisition is initialization)。
QScopedPointer保证指向的对象在当前范围消失时自动删除。
下面的函数分配了堆内存,使用后手动删除。

void myFunction(bool useSubClass)
 {
     MyClass *p = useSubClass ? new MyClass() : new MySubClass;
     QIODevice *device = handsOverOwnership();

     if (m_value > 3) {
         delete p;
         delete device;
         return;
     }

     try {
         process(device);
     }
     catch (...) {
         delete p;
         delete device;
         throw;
     }

     delete p;
     delete device;
 }

使用QScopedPointer可以简化上面的代码:

void myFunction(bool useSubClass)
 {
     // assuming that MyClass has a virtual destructor
     QScopedPointer<MyClass> p(useSubClass ? new MyClass() : new MySubClass);
     QScopedPointer<QIODevice> device(handsOverOwnership());

     if (m_value > 3)
         return;

     process(device);
 }

编译器为QScopedPointer生成的代码与手动编写的代码相同。delete代码是QScopedPointer的选项之一。QScopedPointer没有赋值构造函数或赋值操作,以便清楚的表达所有权和生命周期。
C++ 中const修饰的指针也可以使用QScopedPointer来表示。

    const QWidget *const p = new QWidget();
    // 等价于
    const QScopedPointer<const QWidget> p(new QWidget());

    QWidget *const p = new QWidget();
    // 等价于
    const QScopedPointer<QWidget> p(new QWidget());

    const QWidget *p = new QWidget();
    // 等价于
    QScopedPointer<const QWidget> p(new QWidget());

自定义清理处理器:
使用malloc分配的数组指针不能使用delete删除,QScopedPointer的第二个模板参数可以用于自定义清理处理器。QT提供了以下自定义清理处理器:

  • QScopedPointerDeleter 默认处理器,使用delete删除指针。
  • QScopedPointerArrayDeleter 使用delete []删除。用于处理使用new [] 创建的指针。
  • QScopedPointerPodDeleter 使用free()删除指针。用于处理使用malloc()创建的指针。
  • QScopedPointerDeleteLater 使用deleteLater()删除指针。用于处理QEventLoop中使用的QObject指针。
    自定义清理处理器中必须提供一个public的静态函数:public static function void cleanup(T *pointer)
 // QScopedPointer 使用 delete[] 删除数据
 QScopedPointer<int, QScopedPointerArrayDeleter<int> > arrayPointer(new int[42]);

 // QScopedPointer 使用 free()释放内存
 QScopedPointer<int, QScopedPointerPodDeleter> podPointer(reinterpret_cast<int *>(malloc(42)));

 // 该 struct 调用 "myCustomDeallocator" 删除指针
 struct ScopedPointerCustomDeleter
 {
    static inline void cleanup(MyCustomClass *pointer)
    {
        myCustomDeallocator(pointer);
    }
 };

 // QScopedPointer 使用自定义清理处理器:
 QScopedPointer<MyCustomClass, ScopedPointerCustomDeleter> customPointer(new MyCustomClass);

QScopedPointer可以使用前置声明的类,但是要保证QScopedPointer需要清理的时候前置声明的类的析构函数可用。否则,编译器将输出析构函数不可用的警告。

 class MyPrivateClass; // 前置声明 MyPrivateClass

class MyClass
{
private:
    QScopedPointer<MyPrivateClass> privatePtr; // QScopedPointer 使用前置声明类

public:
    MyClass(); // OK
    inline ~MyClass() {} // VIOLATION - 析构函数不能使用inline修饰

private:
    Q_DISABLE_COPY(MyClass) // OK - 设置拷贝构造函数和赋值操作不可用,编译器不会自动生成构造函数和赋值操作
};

成员函数

QScopedPointer::QScopedPointer(T *p = nullptr)

构造 QScopedPointer 实例,并指向p

QScopedPointer::~QScopedPointer()

销毁QScopedPointer对象。删除它指向的对象

T *QScopedPointer::data() const

返回QScopedPointer指向对象的值。QScopedPointer仍然指向该对象

T *QScopedPointer::get() const

与 data()功能相同

bool QScopedPointer::isNull() const

QScopedPointer指向的对象为nullptr时返回true

void QScopedPointer::reset(T *other = nullptr)

删除已指向的对象并指向新对象other

QScopedArrayPointer

QScopedArrayPointer 与 QScopedPointer 类似,但是删除指针时使用的时 delete[] 操作。QScopedArrayPointer 存储的指针指向动态分配的数组对象。如果我们指向的内存数据是一个数组,这时可以用 QScopedArrayPointer。例如:

 void foo()
 {
     QScopedArrayPointer<int> i(new int[10]);
     i[2] = 42;
     ...
     return; // 此时我们定义的 integer 数组会使用 delete[] 自动删除
 }

QSharedPointer

QSharedPointer 相当于C++11 标准中的 shared_ptr, 用于管理动态分配的对象的共享所有权,即多个 QSharedPointer 对象可以指向同一个对象,并共享该对象的内存管理。它使用引用计数来追踪对象的使用情况,当最后一个 QSharedPointer 对象被销毁时,它将自动删除它所持有的指针。由于使用了引用计数,QSharedPointer 能够自动处理指针的生命周期,避免内存泄漏和空悬指针等问题,因此是Qt中最常用的智能指针。

需要注意的是,QSharedPointer只能管理动态分配的对象的内存。如果我们将其用于指向栈对象或全局对象,那么它就不会自动释放对象的内存,这可能会导致程序崩溃或内存泄漏。

创建 QSharedPointer 对象可以用普通指针、另一个 QSharedPointer 对象,也可以通过将 QWeakPointer 对象提升为强引用来创建。

线程安全

QSharedPointer 和 QWeekPointer 是可重入类,如果不进行同步,多个线程无法同时访问指定的 QSharedPointer 对象或 QWeakPointer 对象。

多个 QSharedPointer 和 QWeekPointer 指向同一个对象的情况下,多个线程可以安全的访问这些 QSharedPointer 和 QWeekPointer对象。QSharedPointer 采用的引用计数机制是原子操作,不需要手动同步。但是要注意,QSharedPointer 和 QWeekPointer 所指向的对象不一定是线程安全的,需要采用线程安全和重入规则来保证 QSharedPointer 和 QWeekPointer 所指向对象的安全。

其他指针类

Qt还提供了另外两个指针包装类: QPointer 和 QSharedDataPointer。它们彼此不兼容,因为每个都有其非常不同的用例。

QSharedPointer 通过外部引用计数(即放置在对象外部的引用计数器)持有共享指针,指针值在 QSharedPointer 和 QWeekPointer 的所有实例之间共享。但是,指针指向的对象不应被视为共享的:都是同一个对象。QSharedPointer 不提供detach(隐式共享)或拷贝所引用对象的方法。

QSharedDataPointer 通过基类 QSharedData 内的引用计数持有共享数据的指针(共享数据派生自 QSharedData 类)。QSharedDataPointer 可以根据访问类型对受保护数据执行detach(隐式共享):如果不是读访问,则以原子方式创建一个副本以完成操作。

QExplicitlySharedDataPointer 是 QSharedDataPointer 的一个变量,QSharedDataPointer只有在 QExplicitlySharedDataPointer::detach() 时才会执行detach。

QScopedPointer 专为堆内分配、删除对象设计,它持有指向堆分配对象的指针,并在其析构函数中删除对象。 QScopedPointer 是轻量级的,它不使用额外的结构或引用计数。

QPointer 用于持有 QObject 派生对象的指针,但是一种弱引用。QWeakPointer 具有相同的功能,但不推荐使用该功能。

可选指针跟踪
编译调试时可以开启 QSharedPointer 的指针跟踪功能。启用后,QSharedPointer 会在全局集合中注册它跟踪的所有指针。这样就可以捕获错误(例如:将同一指针分配给两个 QSharedPointer 对象)。

开启指针跟踪功能需要在 include QSharedPointer 前定义宏QT_SHAREDPOINTER_TRACK_POINTERS。

即使没有启用指针跟踪功能进行代码编译,使用 QSharedPointer 跟踪指针也是安全的。如果编译的代码没有开启指针跟踪功能,QSharedPointer 会从跟踪器中删除指针。

注意,指针跟踪功能对多重继承或虚拟继承有限制(此时两个不同的指针可以引用同一对象)。在这种情况下,如果指针被强制转换为不同的类型并且其值发生更改,则 QSharedPointer 的指针跟踪机制可能无法检测到被跟踪的对象是否为同一对象。

QWeakPointer

QWeakPointer 是对指针的弱引用,相当于C++11 标准中的 weak_ptr。QWeakPointer 不影响指针引用计数,可以用于验证指针是否在已另一个上下文中被删除。QWeakPointer对象的创建只能通过QSharedPointer赋值。QWeakPointer 用于追踪指针,但并不代表指针本身,它不保证指针对象的有效性,也不提供转换操作。如果要访问 QWeakPointer 追踪的指针,需要向将其提升为 QSharedPointer 并验证该对象是否为 null,若对象不为 null,则可以使用该指针。QWeakPointer::toStrongRef()用于将 QWeakPointer 转换为 QSharedPointer。
QWeakPointer 指向 QSharedPointer 所管理的对象,但不会增加对象的引用计数,也不会影响对象的生命周期。当对象被释放时,QWeakPointer会自动被置为空指针,避免了空悬指针的问题。

#include <QSharedPointer>
#include <QWeakPointer>
#include <QDebug>

class MyClass
{
public:
    MyClass(int value) : m_value(value) {
        qDebug() << "MyClass constructor called with value" << m_value;
    }
    ~MyClass() {
        qDebug() << "MyClass destructor called with value" << m_value;
    }
    int getValue() const {
        return m_value;
    }

private:
    int m_value;
};

int main()
{
    QSharedPointer<MyClass> shared(new MyClass(20));
    QWeakPointer<MyClass> weak(shared);

    qDebug() << "Shared pointer value:" << shared->getValue();
    qDebug() << "Weak pointer value:" << weak.data()->getValue();

    shared.clear();
    // 此时,MyClass对象的引用计数为0,将被自动删除,而此时 QWeakPointer 对象 weak 也为null。

    if (weak.isNull()) {
        qDebug() << "Weak pointer is null - object has been deleted"; //执行
    } else {
        qDebug() << "Weak pointer is not null - object still exists";
    }

    return 0;
}

QSharedDataPointer

QSharedDataPointer 用于简化隐式共享类的实现。QSharedDataPointer 实现了线程安全的引用计数,保证了向可重入类添加 QSharedDataPointers 时不破坏的的可重入性。Qt 中很多类都采用了隐式共享来提升指针的访问速度和内存使用效率。Qt 的容器类都使用了隐式共享,如 QList 、QVarLengthArray 、QStack 、QQueue、 QSet、 QMap、 QMultiMap、 QHash、 QMultiHash 。
例如,实现 Employee 类的隐式共享,需要以下步骤:

  1. 定义 Employee 类,并声明一个 QSharedDataPointer 成员变量;
  2. 定义一个 EmployeeData 并继承 QSharedData,将 Employee 需要的成员变量在 EmployeeData 中进行声明。
#include <QSharedData>
#include <QString>

// EmployeeData 继承自 QSharedData,QSharedData为其提供了一个引用计数器
// EmployeeData 要实现共享必须提供默认的构造函数、拷贝构造函数和析构函数
// 如果要对 Employee 类的使用者隐藏数据,应该把 EmployeeData 类声明到独立的 .h 文件中
class EmployeeData : public QSharedData
{
  public:
    EmployeeData() : id(-1) { }
    // 拷贝构造函数
    EmployeeData(const EmployeeData &other)
        : QSharedData(other), id(other.id), name(other.name) { }
    ~EmployeeData() { }

    // 共享数据
    int id;
    QString name;
};

class Employee
{
  public:
    Employee() { d = new EmployeeData; }
    // 该构造函数调用了 setId(id) 和 setName(name), 2次修改共享数据,但是并不会触发 copy on write
    Employee(int id, const QString &name) {
        d = new EmployeeData;
        setId(id); // 此时 EmployeeData 的引用计数为 1
        setName(name); // 引用计数为 1 ,不会 copy 共享数据
    }
    // 这个拷贝构造函数可以不用定义,编译器会自动生成拷贝构造函数
    // 拷贝构造函数会对成员变量 d 进行赋值,赋值操作 =() 会增加共享数据的引用计数
    Employee(const Employee &other)
          : d (other.d)
    {
    }
    // 修改数据时,操作符 ->() 会自动调用 detach() ,如果共享数据的引用计数大于1,会自动copy一份数据用于修改。
    // 保证一个 Employee 对象对共享数据的修改不会影响其它Employee对象引用的共享数据
    void setId(int id) { d->id = id; }
    void setName(const QString &name) { d->name = name; }

    // 不修改 EmployeeData ,操作符 -> 不会触发detach()
    int id() const { return d->id; }
    QString name() const { return d->name; }

  private:
    // Employee 只用一个成员变量,其它数据在 EmployeeData 中定义
    QSharedDataPointer<EmployeeData> d;
};

在上面的场景中,Employee 对象拷贝、赋值或作为参数传递时,QSharedDataPointer 自动增加引用计数。Employee 对象删除或不在当前范围时, QSharedDataPointer 自动减少引用计数。当引用计数为 0 时,EmployeeData 对象自动被删除。当 Employee 的成员函数对成员变量 d 进行修改时,QSharedDataPointer 自动调用 detach() 操作,并为 Employee 对象拷贝出一份数据,保证 Employee 对象对数据的修改不会影响其它对象。如果 Employee 有多个成员函数都会修改成员变量 d,detach() 会被调用多次,但是只有首次调用 detach() 时才会拷贝共享数据,因为第一次调用 detach() 后成员变量 d 指向新拷贝的数据,它的引用计数为 1。

隐式共享需要注意的问题:
看下面的代码,如果 e1 和 e2 都代表 id 为 1001 的数据,那么将出现 1001 有 2 个不同 name 的问题,这在数据一致性上可能会有问题。这种问题需要使用显示共享来解决,即在 Employee 类中声明为显示共享 QExplicitlySharedDataPointer d; 显示共享不会自动执行 copy on write 操作。

#include "employee.h"

int main()
{
    Employee e1(1001, "Albrecht Durer");
    // e1 和 e2 指向同一个共享数据(1001, "Albrecht Durer")
    Employee e2 = e1;
    // setName 后成员变量 e1 将指向另一个数据(1001, "Hans Holbein")
    e1.setName("Hans Holbein");
}

可以考虑使用宏Q_DECLARE_TYPEINFO()将隐式共享类声明为 movable 类型,例如上面的 Employee 类。

隐式共享的 iterator 问题:当使用 iterator 遍历容器时,不能进行容器拷贝。例如:

QList<int> a, b;
// 构造一个用 0 初始化的 list
a.resize(100000); 

QList<int>::iterator i = a.begin();
// 下面是 iterator i 的错误使用方式

b = a;

// 与 STL 容器不同的是,如果此时执行 *i = 4 会发生隐式拷贝

a[0] = 5;
// 此时 a 已经从共享数据中分离出来,虽然 i 是 容器 a 的遍历器,但实际上是 b 的遍历器,此时 (*i) == 0

b.clear(); 
// 此时遍历器 i 已经完全无效了

int j = *i; 
// 此时会出现 i 未定义的情况

// 如果使用的是 STL 的容器类 std::list<T> 不会发生上述情况,此时 (*i) == 5

QExplicitlySharedDataPointer

QExplicitlySharedDataPointer 用于简化显式共享类的实现。QExplicitlySharedDataPointer 实现了线程安全的引用计数,保证了向可重入类添加 QSharedDataPointers 时不破坏的的可重入性。
QExplicitlySharedDataPointer 与 QSharedDataPointer 类似,惟一不同的是 QExplicitlySharedDataPointer 的成员函数在出现写操作时不会自动 copy 共享数据。QExplicitlySharedDataPointer 的 detach() 方法只能手动调用。QExplicitlySharedDataPointers 自动进行引用计数并在引用计数变为 0 时删除共享数据。

参考文章:
Qt 智能指针介绍: QSharedPointer、QWeakPointer 、QScopedPointer 、QPointer(附实例)
Qt 中的智能指针
【图解】Qt中的智能指针
Qt 智能指针学习(7种QT的特有指针)文章来源地址https://www.toymoban.com/news/detail-842423.html

到了这里,关于QT 智能指针 QPointer QScopedPointer QSharedPointer QWeakPointer QSharedDataPointer 隐式共享 显示共享的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 写个简单的管理数组指针的智能指针

    模板智能数组指针 1.管理任意类型的数组指针 2.释放的时候自动删除数组指针指向的内存 3.通过指针计算数组中的个数   3.要有指针的样式和数组的样式 4.支持for范围查询和迭代器 5.实例用法  

    2024年02月08日
    浏览(51)
  • QT6之类实例化——对象指针和对象

    Qt完全遵循C++ 中类的实例化动作按存储位置可以分为 栈中分配内存 和 堆中分配内存 两种,分别对应不用 new 实例化类和用 new 实例化类。 一、实例化两种方式 1、栈中分配; 如下图是qt非常常见的操作,将m_view声明为对象,它完全表明该对象的生命周期与MyWidget的生命周期相

    2023年04月25日
    浏览(50)
  • Qt(C++)绘制指针仪表盘显示当前温度

    当前文章要实现的功能: 使用Qt绘制一个仪表盘,用来显示当前的温度,绘制刻度、绘制数字、绘制温度指针。仪表盘全程使用QPainter进行绘制,QPainter是Qt框架中非常重要的一个类,绘制功能的实现离不开它。如果想要使用Qt进行高质量的绘图或UI设计,必须掌握QPainter的使用

    2024年02月07日
    浏览(44)
  • 【重学C++】02 脱离指针陷阱:深入浅出 C++ 智能指针

    【重学C++】02 脱离指针陷阱:深入浅出 C++ 智能指针 大家好,今天是【重学C++】系列的第二讲,我们来聊聊C++的智能指针。 在上一讲《01 C++如何进行内存资源管理》中,提到了对于堆上的内存资源,需要我们手动分配和释放。管理这些资源是个技术活,一不小心,就会导致内

    2024年02月05日
    浏览(60)
  • c++--智能指针简单描述

    1.什么是智能指针 智能指针,指该指针用于确保程序不存在内存和资源泄漏且是异常安全的。 智能指针是你在堆栈上声明的类模板,并可通过使用指向某个堆分配的对象的原始指针进行初始化。 在初始化智能指针后,它将拥有原始的指针。 这意味着智能指针负责删除原始指

    2024年02月12日
    浏览(40)
  • [C++11] 智能指针

             长路漫漫,唯剑作伴。         目录          长路漫漫,唯剑作伴。         为什么需要智能指针 RAII 使用RAII思想管理内存 重载 * 和- 总结一下智能指针的原理: C++的智能指针和拷贝问题 auto_ptr        (C++98)   ​编辑 auto_ptr的实现原理:管理权转移

    2024年02月09日
    浏览(36)
  • C++新特性:智能指针

    智能指针主要解决以下问题: 1)内存泄漏:内存手动释放,使用智能指针可以自动释放 2)共享所有权指针的传播和释放,比如多线程使用同一个对象时析构问题,例如同样的数据帧,但是业务A和业务B处理的逻辑不一样(都是只读)。可以用 shared_ptr 共享数据帧对象的所有

    2024年02月09日
    浏览(43)
  • Rust- 智能指针

    A smart pointer is a data structure that not only acts like a pointer but provides additional functionality. This “smartness” comes from the fact that smart pointers encapsulate additional logical or semantic rules, which are automatically applied to simplify memory or resource management tasks. While different programming languages implement smart point

    2024年02月14日
    浏览(45)
  • 【C++11】智能指针

    什么是智能指针:         智能指针是一个 类 ,用来存储指向动态分配对象的指针,负责 自动释放 动态分配的对象,防止 堆内存泄漏 。动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源。  裸指针的缺点: 1.难区分执行一个

    2024年02月06日
    浏览(32)
  • C++11:智能指针

    头文件memory 名称空间 std what is 智能指针? 诸如 auto_ptr 以及 C++11 新增的 shared_ptr 和 unique_ptr 等智能指针模板使得管理由 new 分配的内存更容易。它们是类。如果使用这些智能指针(而不是常规指针)来保存 new 返回的地址,则不必在以后使用删除运算符。智能指针对象过期时

    2024年02月20日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包