Qt 中的智能指針(轉)


Qt 中的智能指針

上一篇博客中介紹了 C++11 標准中的提供的智能指針。在 Qt 中也提供了類似的替代功能,並且比 C++11 標准中提供的功能還要強大,所以如果我們使用 Qt 作為基礎庫,那么就沒有必要使用C++11 的智能指針。

Qt 的智能指針包括:

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 工作,像一個旁觀者一樣來觀測資源的使用情況。

 

(轉自:https://blog.csdn.net/liyuanbhu/article/details/77825655?utm_medium=distribute.pc_relevant.none-task-blog-title-1&spm=1001.2101.3001.4242)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM