欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入理解Qt 智能指針

 更新時間:2024年01月19日 15:03:16   作者:zhaoyongCNSX  
智能指針是一種特殊的指針,可以自行管理和釋放資源,防止內(nèi)存泄漏和懸掛指針,本文主要介紹了深入理解Qt 智能指針,具有一定的參考價值,感興趣的可以了解一下

1. Qt智能指針概述

  • Qt 提供了一套基于父子對象的內(nèi)存管理機(jī)制, 所以我們很少需要去手動 delete. 但程序中不一定所有類都是QObject的子類, 這種情況下仍然需要使用一些智能指針.
  • 注意: 在 Qt 中使用智能指針時, 一定要避免發(fā)生多次析構(gòu).

2. Qt中的智能指針分類

根據(jù)不同的使用場景, 可分為以下幾種:

  • 共享數(shù)據(jù). 隱式或顯式的共享數(shù)據(jù)(不共享指針), 也被稱為 侵入式指針.
    • QSharedDataPointer 指向隱式共享對象的指針.
    • QExplicitlySharedDataPointer 指向顯式共享對象的指針.
  • 共享指針. 線程安全.
    • QSharedPointer. 有點(diǎn)像 std::shared_ptrboost::shared_ptr. 維護(hù)引用計數(shù), 使用上最像原生指針.
    • QWeakPointer, 類似于boost::weak_ptr. 作為 QSharedPointer 的助手使用. 未重載*->. 用于解決強(qiáng)引用形成的相互引用.
  • 范圍指針. 為了RAII1目的, 維護(hù)指針?biāo)袡?quán), 并保證其在超出作用域后恰當(dāng)?shù)谋讳N毀, 非共享.
    • QScopedPointer. 相當(dāng)于 std::unique_ptr.
      • 所有權(quán)唯一, 其拷貝和賦值操作均為私有. 無法用于容器中.
    • QScopedArrayPointer
  • 追蹤給定 QObject 對象生命, 并在其析構(gòu)時自動設(shè)置為 NULL.
    • QPointer.

3. 共享數(shù)據(jù)

  • 共享數(shù)據(jù)是為了實(shí)現(xiàn) “讀時共享, 寫時復(fù)制”. 其本質(zhì)上是延遲了 執(zhí)行深拷貝 的時機(jī)到了需要修改其值的時候.
  • C++實(shí)現(xiàn)為 在拷貝構(gòu)造和賦值運(yùn)算符函數(shù)中不直接深度拷貝, 而是維護(hù)一個引用計數(shù)并獲得一個引用或指針. 在需要改變值的方法中再執(zhí)行深度拷貝.
  • 隱式共享為, 我們無需管理深度拷貝的時機(jī), 它會自動執(zhí)行.
  • 顯式共享為, 我們需要人為判斷什么時候需要深度拷貝, 并手動執(zhí)行拷貝.
  • QSharedData 作為共享數(shù)據(jù)對象的基類. 其在內(nèi)部提供 線程安全的引用計數(shù).
  • 其與 QSharedDataPointer 和 QExplicitlySharedDataPointer 一起使用.
  • 以上三個類都是可重入的.

3.1. 隱式共享

  • QSharedDataPointer表示指向隱式共享對象的指針.
  • 其在寫操作時, 會自動調(diào)用detach(). 該函數(shù)在當(dāng)共享數(shù)據(jù)對象引用計數(shù)大于1, 會執(zhí)行深拷貝, 并將該指針指向新拷貝內(nèi)容. (這是在該類的非 const 成員函數(shù)中自動調(diào)用的, 我們使用時不需要關(guān)心).

比如, 我們現(xiàn)在有一個類 MyClass, 現(xiàn)在要將其改造成支持隱式共享的類.

#if 1   // MyClass 原始版本
class MyClass {
public:
	MyClass(){}
	MyClass(const MyClass &other) {
		m_id = other.GetId();
		m_path = other.GetPath();
	}
	~MyClass(){}

	int GetId() const { return m_id; }
	void SetId(int val) { m_id = val; }
	QString GetPath() const { return m_path; }
	void SetPath(QString val) { m_path = val; }
private:
	int m_id = -1;
	QString m_path;
};

#else // MyClass 支持隱式共享
// 1. 將 MyClass 的所有數(shù)據(jù)成員都放到 MyClassData 中. 
// 2. 在 MyClass 中維護(hù)一個 QSharedDataPointer<MyClassData> d.
// 3. MyClass 中通過 d-> 的形式訪問數(shù)據(jù).
// 4. MyClassData 繼承自 QSharedData.

#include <QSharedData>
#include <QSharedDataPointer>
class MyClassData : public QSharedData
{
public:
	MyClassData(){}
	MyClassData(const MyClassData &other)
		: QSharedData(other), id(other.id), path(other.path) {}
	~MyClassData(){}

	int id = -1;
	QString path;
};

class MyClass {
public:
	MyClass(){ d = new MyClassData(); }
	MyClass(int id, const QString & path) {
		d = new MyClassData();
		SetId(id);
		SetPath(path);
	}
	MyClass(const MyClass &other) : d(other.d){}
	~MyClass(){}

	int GetId() const { return d->id; }
	void SetId(int val) { d->id = val; }
	QString GetPath() const { return d->path; }
	void SetPath(QString val) { d->path = val; }
private:
	QSharedDataPointer<MyClassData> d;
};
#endif

3.2. 顯式共享

  • QExplicitlySharedDataPointer 表示指向顯式共享對象的指針.
  • QSharedDataPointer不同的地方在于, 它不會在非const成員函數(shù)執(zhí)行寫操作時, 自動調(diào)用 detach().
  • 所以需要我們在寫操作時, 手動調(diào)用 detach().
  • 它的行為很像 C++ 常規(guī)指針, 不過比指針好的地方在于, 它也維護(hù)一套引用計數(shù), 當(dāng)引用計數(shù)為0時會自動設(shè)置為 NULL. 避免了懸空指針的危害.

修改上面例子中用到的 QSharedDataPointer 為 QExplicitlySharedDataPointer.

注意: 如果在使用時發(fā)現(xiàn)所有寫操作的函數(shù)中都調(diào)用了 detach(), 那就可以直接使用 QSharedDataPointer 了.

#include <QExplicitlySharedDataPointer>
class MyClass {
public:
	MyClass(){ d = new MyClassData(); }
	MyClass(int id, const QString & path) {
		d = new MyClassData();
		SetId(id);
		SetPath(path);
	}
	MyClass(const MyClass &other) : d(other.d){}
	~MyClass(){}

	int GetId() const { return d->id; }
	void SetId(int val) { 
		// 需要手動調(diào)用 detach()
		d.detach();
		d->id = val; 
	}
	QString GetPath() const { return d->path; }
	void SetPath(QString val) { 
		// 需要手動調(diào)用 detach()
		d.detach();
		d->path = val; 
	}
private:
	QExplicitlySharedDataPointer<MyClassData> d;
};

4. 共享指針

4.1. QSharedPointer

  • 可用于容器中.
  • 可提供自定義 Deleter, 所以可用于 delete [] 的場景.
  • 線程安全. 多線程同時修改其對象無需加鎖. 但其指向的內(nèi)存不一定線程安全, 所以多線程同時修改其指向的數(shù)據(jù), 還需要加鎖.
// 指定 Deleter
static void doDeleteLater(MyObject *obj)
{
    obj->deleteLater();
}

void TestSPtr()
{
    QSharedPointer<MyObject> obj(new MyObject, doDeleteLater);

    // 調(diào)用 clear 清除引用計數(shù), 并調(diào)用 Deleter 刪除指針對象.
    // 此處將調(diào)用 doDeleteLater
    obj.clear();

    QSharedPointer<MyObject> pObj1 = obj;
    pObj1->show();

    if (pObj1) {

    }
}

/*------------- 實(shí)現(xiàn)單例 -------------*/
// cpp 中定義全局變量
QSharedPointer<MyObject> g_ptrMyObj;

QSharedPointer<MyObject> GetMyObj() {
    if (g_ptrMyObj.data() == NULL) {
        g_ptrMyObj.reset(new MyObject());
    }
    return g_ptrMyObj;
}

4.2. QWeakPointer

  • 創(chuàng)建: 只能通過 QSharedPointer 的賦值來創(chuàng)建.
  • 使用: 不可直接使用. 沒有重載*->. 需要用 toStrongRef() 轉(zhuǎn)換為 QSharedPointer, 并判斷是否為 NULL 之后再使用.
    • 可使用 data() 取到指針值, 但不確保其有效. 如果想使用該值, 需用戶在外部使用其他手段保證其指針值有效.
    • 曾經(jīng)有一段時間, Qt 官方想用 QWeakPointer 取代 QPointer, 但在 Qt5 重寫了 QPointer 后, 就不再這么建議了.
    • 所以使用 QWeakPointer 的最佳場景仍然是: 作為 QSharedPointer 的助手類使用.

打開宏 TEST_memory_will_leak, 則因?yàn)楫a(chǎn)生循環(huán)引用, 導(dǎo)致內(nèi)存泄漏, 表現(xiàn)為: 不會打印 “destruct A”, “destruct B”

關(guān)閉宏, 使用 QWeakPointer, 不會產(chǎn)生內(nèi)存泄漏.

#include <QSharedPointer>
#include <QWeakPointer>

class A;
class B;

#define TEST_memory_will_leak

class A {
public:
	~A() {
		qDebug() << "destruct A";
	}
#ifdef TEST_memory_will_leak
	QSharedPointer<B> ptr_B;
#else
	QWeakPointer<B> ptr_B;
#endif //TEST_memory_will_leak
};


class B {
public:
	~B() {
		qDebug() << "destruct B";
	}
#ifdef TEST_memory_will_leak
	QSharedPointer<A> ptr_A;
#else
	QWeakPointer<A> ptr_A;
#endif //TEST_memory_will_leak
};


int main()
{
	QSharedPointer<A> nA(new A());
	QSharedPointer<B> nB(new B());

    // 若內(nèi)部使用 QSharedPointer, 則此處會形成循環(huán)引用.
	nA->ptr_B = nB;
	nB->ptr_A = nA;

#ifdef TEST_memory_will_leak
	if (!nA->ptr_B.isNull()) {
		qDebug() << "use shared ptr";
	}
#else
	if (!nA->ptr_B.toStrongRef().isNull()) {
		qDebug() << "use weak ptr";
	}
#endif TEST_memory_will_leak
}

5. 范圍指針

void main() 
{
    {
        QScopedPointer<MyClass> p(new MyClass());
        p->func();
    } // 退出作用域后析構(gòu)

    {
        QScopedArrayPointer<int> p(new int[10] );

        p[1] = 10;
    } // 退出作用域后析構(gòu)
}

6. 追蹤特定QObject對象生命

[***以下描述可搜索 DevBean 的 continue-using-qpointer 一文獲取更詳細(xì)信息***].

  • QPointer 在某幾個 Qt5 版本中, 被標(biāo)注為廢棄. 且打算使用 QWeakPointer 來代替其原有功能. (為此還允許 QWeakPointer 可獨(dú)立于 QSharedPointer 使用, 并增加了一系列接口, 引發(fā)了接口歧義的副作用). 但經(jīng)過對 QPointer 的重寫后, 解決了之前性能問題, 所以便移除了廢棄標(biāo)志, 并取消了 QWeakPointer 獨(dú)立使用的相關(guān)描述.
  • 之后還是繼續(xù)將 QWeakPointer 和 QSharedPointer 捆綁使用. 并繼續(xù)愉快的使用 QPointer 吧.
class MyHelper{
public:
    MyHelper(QPushButton *btn)
        : m_btn(btn)
        , m_btn2(NULL) {}

    void SetBtn(QPushButton *btn) {
        m_btn2 = btn;
    }

    void FuncShow() {
        // 當(dāng)外部的 QPushButton 析構(gòu)后, 該值自動設(shè)置為 NULL
        if (m_btn) {
            m_btn->show();
        }

        if (m_btn2) {
            m_btn2->show();
        }
    }
private:
    QPointer<QPushButton> m_btn;
    QPointer<QPushButton> m_btn2;
};

RAII, Resource Acquisition Is Initialization, 資源獲取就是初始化. 是 C++ 的一種管理資源, 避免泄漏的慣用方法. 比如, QMutexLocker為了方便管理QMutex的加鎖和解鎖, 在構(gòu)造該對象時加鎖, 在析構(gòu)時解鎖. 

到此這篇關(guān)于深入理解Qt 智能指針的文章就介紹到這了,更多相關(guān)Qt 智能指針內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • c++版線程池和任務(wù)池示例

    c++版線程池和任務(wù)池示例

    這篇文章主要介紹了c++版線程池和任務(wù)池,實(shí)現(xiàn)任務(wù)執(zhí)行完畢線程退出.在linux下壓力測試通過
    2014-03-03
  • C語言實(shí)現(xiàn)跨文件傳輸數(shù)據(jù)的幾種方式

    C語言實(shí)現(xiàn)跨文件傳輸數(shù)據(jù)的幾種方式

    C語言是一種強(qiáng)大的、通用的編程語言,常用于系統(tǒng)級編程,包括硬件交互,如中斷處理和數(shù)據(jù)采集,在本文中,我們將深入探討如何使用C語言進(jìn)行跨文件數(shù)據(jù)傳輸,文中有相關(guān)的代碼供大家參考,需要的朋友可以參考下
    2024-08-08
  • C語言直接插入排序算法介紹及示例

    C語言直接插入排序算法介紹及示例

    插入排序是把一個記錄插入到已排序的有序序列中,使整個序列在插入該記錄后仍然有序。插入排序中較簡單的種方法是直接插入排序,其插入位置的確定方法是將待插入的記錄與有序區(qū)中的各記錄自右向左依次比較其關(guān)鍵字值的大小
    2022-08-08
  • C字符串操作函數(shù)實(shí)現(xiàn)方法小結(jié)

    C字符串操作函數(shù)實(shí)現(xiàn)方法小結(jié)

    這篇文章主要介紹了C字符串操作函數(shù)實(shí)現(xiàn)方法,實(shí)例總結(jié)了C語言字符串操作的相關(guān)技巧,非常具有實(shí)用價值,需要的朋友可以參考下
    2015-04-04
  • 基于select、poll、epoll的區(qū)別詳解

    基于select、poll、epoll的區(qū)別詳解

    本篇文章是對select、poll、epoll之間的區(qū)別進(jìn)行了詳細(xì)的分析介紹。需要的朋友參考下
    2013-05-05
  • C語言可變參數(shù)與函數(shù)參數(shù)的內(nèi)存對齊詳解

    C語言可變參數(shù)與函數(shù)參數(shù)的內(nèi)存對齊詳解

    這篇文章主要為大家詳細(xì)介紹了C語言可變參數(shù)與函數(shù)參數(shù)的內(nèi)存對齊,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • C++中的boost::function庫簡介

    C++中的boost::function庫簡介

    這篇文章介紹了C++中的boost::function庫,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • C++中std::shuffle的使用小結(jié)

    C++中std::shuffle的使用小結(jié)

    std::shuffle是C++標(biāo)準(zhǔn)庫中的一個函數(shù),用于對容器中的元素進(jìn)行隨機(jī)排列,本文主要介紹了C++中std::shuffle的使用小結(jié),具有一定的參考價值,感興趣的可以了解一下
    2025-04-04
  • 怎么用C++提取任意一張圖片的特征(從內(nèi)存讀取數(shù)據(jù))

    怎么用C++提取任意一張圖片的特征(從內(nèi)存讀取數(shù)據(jù))

    這篇文章主要介紹了用C++提取任意一張圖片的特征(從內(nèi)存讀取數(shù)據(jù))的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • vs2019配置Qt5開發(fā)環(huán)境(圖文教程)

    vs2019配置Qt5開發(fā)環(huán)境(圖文教程)

    本文主要介紹了如何使用visual studi2019配置qt5開發(fā)環(huán)境,以及創(chuàng)建qt項(xiàng)目,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12

最新評論