Qt 中的智能指针

来源:互联网 发布:qq不加群群发软件 编辑:程序博客网 时间:2024/06/01 10:35

Qt 中的智能指针

上一篇博客中介绍了 C++11 标准中的提供的智能指针。在 Qt 中也提供了类似的替代功能,并且比 C++11 标准中提供的功能还要强大,所以如果我们使用 Qt 作为基础库,那么就没有必要使用C++11 的智能指针。

Qt 的智能指针包括:

  • QSharedPointer
  • QScopedPointer
  • QScopedArrayPointer
  • QWeakPointer
  • QPointer
  • QSharedDataPointer

QSharedPointer

QSharedPointer 大体相当于C++11 标准中的 shared_ptr。是在 Qt 4.5 中引入的,所以只要我们的 Qt 版本大于 Qt 4.5 就可以使用这个类。

要使用这个智能指针类,首先要包含对应的头文件:

#include <QSharedPointer>

QSharedPointer 内部维持着对拥有的内存资源的引用计数。比如有 5个 QSharedPointer 拥有同一个内存资源,那么这个引用计数就是 5。这时如果一个 QSharedPointer 离开了它的作用域,那么就还剩下 4 个 QSharedPointer 拥有这个内存资源,引用计数就变为了 4。 当引用计数下降到 0 时,这个内存资源就被释放了。

QSharedPointer 的构造函数有如下这几种。

QSharedPointer();QSharedPointer(X *ptr);QSharedPointer(X *ptr, Deleter deleter);QSharedPointer(std::nullptr_t);QSharedPointer(std::nullptr_t, Deleter d);QSharedPointer(const QSharedPointer<T> &other);QSharedPointer(const QWeakPointer<T> &other);

因此我们可以:
- 构造一个空的 QSharedPointer
- 通过一个普通指针来构造一个 QSharedPointer
- 用另一个 QSharedPointer 来构造一个 QSharedPointer
- 用一个 QWeakPointer 来构造一个 QSharedPointer

并且,我们可以指定 Deleter。因此 QSharedPointer 也可以用于指向 new[] 分配的内存。

QSharedPointer 是线程安全的,因此即使有多个线程同时修改 QSharedPointer 对象也不需要加锁。这里要特别说明一下,虽然 QSharedPointer 是线程安全的,但是 QSharedPointer 指向的内存区域可不一定是线程安全的。所以多个线程同时修改 QSharedPointer 指向的数据时还要应该考虑加锁的。

下面是个 QSharedPointer 的例子,演示了如何自定义 Deleter。

  static void doDeleteLater(MyObject *obj)  {      obj->deleteLater();  }  void otherFunction()  {      QSharedPointer<MyObject> obj = QSharedPointer<MyObject>(new MyObject, doDeleteLater);      // continue using obj      obj.clear();    // calls obj->deleteLater();  }

QSharedPointer 使用起来就像是普通的指针。唯一让我不满意的地方就是 QSharedPointer 不支持指向一个数组。

QScopedPointer

QScopedPointer 类似于 C++ 11 中的 unique_ptr。当我们的内存数据只在一处被使用,用完就可以安全的释放时就可以使用 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);  }

QScopedArrayPointer

如果我们指向的内存数据是一个数组,这时可以用 QScopedArrayPointer。QScopedArrayPointer 与 QScopedPointer 类似,用于简单的场景。

例如下面这个例子:

void foo()  {      QScopedArrayPointer<int> i(new int[10]);      i[2] = 42;      ...      return; // our integer array is now deleted using delete[]  }

QPointer

QPointer 与其他的智能指针有很大的不同。其他的智能指针都是为了自动释放内存资源而设计的。 QPointer 智能用于指向 QObject 及派生类的对象。当一个 QObject 或派生类对象被删除后,QPointer 能自动把其内部的指针设为 0。这样我们在使用这个 QPointer 之前就可以判断一下是否有效了。

为什么非要是 QObject 或派生类呢,因为 QObject 可以构成一个对象树,当这颗对象树的顶层对象被删除时,它的子对象自动的被删除。所以一个 QObject 对象是否还存在,有时并不是那么的明显。有了 QPointer 我们在使用一个对象之前,至少可以判断一下。

要特别注意的是,当一个 QPointer 对象超出作用域时,并不会删除它指向的内存对象。这和其他的智能指针是不同的。

下面给一个简单的例子:

    QPointer<QLabel> label = new QLabel;      label->setText("&Status:");      ...      if (label)          label->show();

QSharedDataPointer

QSharedDataPointer 这个类是帮我们实现数据的隐式共享的。我们知道 Qt 中大量的采用了隐式共享和写时拷贝技术。比如下面这个例子:

QString str1 = "abcdefg";QString str2 = str1;QString str2[2] = 'X';

第二行执行完后,str2 和 str1 指向的同一片内存数据。当第三句执行时,Qt 会为 str2 的内部数据重新分配内存。这样做的好处是可以有效的减少大片数据拷贝的次数,提高程序的运行效率。

Qt 中隐式共享和写时拷贝就是利用 QSharedDataPointer 和 QSharedData 这两个类来实现的。

比如我们有个类叫做 Employee,里面有些数据希望能够利用隐式共享和写时拷贝技术。那么我们可以把需要隐式共享的数据封装到另一个类中,比如叫做 EmployeeData,这个类要继承自 QSharedData。

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;  };

这个例子中,id 和 name 就是我们要隐式共享和写时拷贝的数据。那么 Employee 类需要这么来实现。

class Employee{public:    Employee() { d = new EmployeeData; }    Employee(const Employee &other)            : d (other.d)      {      }    Employee(int id, const QString &name)     {        d = new EmployeeData;        setId(id);        setName(name);    }    Employee(const Employee &other)        : d (other.d)    {    }    void setId(int id) { d->id = id; }    void setName(const QString &name) { d->name = name; }    int id() const { return d->id; }    QString name() const { return d->name; }private:    QSharedDataPointer<EmployeeData> d;};

我们对 Employee 中数据的访问都要通过 d。那么当需要修改 id 或 name 时,QSharedDataPointer 类自动的调用 detach() 方法来完成数据的拷贝。d (other.d) 则完成了隐式共享的功能。

看到了吧,就这么简单。如果这些功能都要自己实现的话,代码量可是不小的。

于这个类有关的还有个 QExplicitlySharedDataPointer 这个类用到的机会更少,所以就不多做介绍了,请大家自己看 Qt 的帮助文档。

QWeakPointer

最后来简单介绍一下 QWeakPointer。这个类用到的机会不多。(说实话我不太会用这个类,下面的介绍都是网上抄的.)

QWeakPointer 是为配合 QSharedPointer 而引入的一种智能指针,它更像是 QSharedPointer 的一个助手(因为它不具有普通指针的行为,没有重载operator*和->)。它的最大作用在于协助 QSharedPointer 工作,像一个旁观者一样来观测资源的使用情况。

原创粉丝点击