最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

融會(huì)貫通C++智能指針教程

 更新時(shí)間:2021年08月30日 14:52:22   作者:只會(huì)寫(xiě)bug~  
本文主要介紹了c++的基礎(chǔ)知識(shí),通過(guò)不帶引用計(jì)數(shù)的只能指針和帶引用的智能指針,shared_ptr和weak_ptr,多線(xiàn)程訪(fǎng)問(wèn)共享對(duì)象的線(xiàn)程安全問(wèn)題以及自定義刪除器作了詳細(xì)的分析解答

一、基礎(chǔ)知識(shí)介紹

裸指針常出現(xiàn)以下幾個(gè)問(wèn)題:

  • 忘記釋放資源,導(dǎo)致資源泄露(常發(fā)生內(nèi)存泄漏問(wèn)題)
  • 同一資源釋放多次,導(dǎo)致釋放野指針,程序崩潰
  • 寫(xiě)了釋放資源的代碼,但是由于程序邏輯滿(mǎn)足條件,執(zhí)行中間某句代碼時(shí)程序就退出了,導(dǎo)致釋放資源的代碼未被執(zhí)行到
  • 代碼運(yùn)行過(guò)程中發(fā)生異常,隨著異常棧展開(kāi),導(dǎo)致釋放資源的代碼未被執(zhí)行到
template<typename T> 
class SmartPtr {
public:
    SmartPtr(T* ptr = nullptr):_ptr(ptr) {}
    ~SmartPtr() {delete _ptr;}
private:
    T* _ptr;
};
int main(){
    SmartPtr<int> ptr(new int);
    return 0;
}

上面這段代碼就是一個(gè)非常簡(jiǎn)單的智能指針,主要用到了這兩點(diǎn):

手動(dòng)實(shí)現(xiàn)智能指針體現(xiàn)在把裸指針進(jìn)行了面向?qū)ο蟮姆庋b,在構(gòu)造函數(shù)中初始化資源地址,在析構(gòu)函數(shù)中負(fù)責(zé)釋放資源

利用棧上的對(duì)象出作用域自動(dòng)析構(gòu)這個(gè)特點(diǎn),在智能指針的析構(gòu)函數(shù)中保證釋放資源。

所以,智能指針一般都是定義在棧上的

面試官:能不能在堆上定義智能指針?
答:不能。就好比SmartPtr<int>* ptr = new SmartPtr<int>();這段代碼中,在堆空間定義一個(gè)智能指針,

這依然需要我們手動(dòng)進(jìn)行delete,否則無(wú)法堆空間的對(duì)象無(wú)法釋放,因?yàn)槎芽臻g的對(duì)象無(wú)法自動(dòng)調(diào)用析構(gòu)函數(shù)。

一般而言智能指針還需要提供裸指針常見(jiàn)的*->兩種運(yùn)算符的重載函數(shù):

    const T& operator*() const{return *_ptr;}
    T& operator*(){return *_ptr;}
    const T operator->() const{return _ptr;}
    T operator->(){return _ptr;}

二、不帶引用計(jì)數(shù)的智能指針

int main(){
    SmartPtr<int> ptr1(new int);
    SmartPtr<int> ptr2(ptr1);
    return 0;
}

以上代碼運(yùn)行時(shí),由于ptr2拷貝構(gòu)造時(shí)默認(rèn)是淺拷貝,會(huì)出現(xiàn)同一資源釋放兩次的錯(cuò)誤(釋放野指針),這里需要解決兩個(gè)問(wèn)題:

智能指針的淺拷貝

多個(gè)智能指針指向同一個(gè)資源的時(shí)候,怎么保證資源只釋放一次,而不是每個(gè)智能指針都釋放一次

不帶引用計(jì)數(shù)的智能指針主要包括

auto_ptr,scoped_ptrunique_ptr

(1)auto_ptr源碼

auto_ptr(auto_ptr& _Right) noexcept : _Myptr(_Right.release()) {}
_Ty* release() noexcept {
    _Ty* _Tmp = _Myptr;
    _Myptr    = nullptr;
    return _Tmp;
}

使用auto_ptr

	auto_ptr<int> ptr1(new int);
    auto_ptr<int> ptr2(ptr1);

從源碼可以看到,auto_ptr底層先是將ptr1置空,然后將指向的資源再給ptr2, auto_ptr所做的就是使最后一個(gè)構(gòu)造的指針指向資源,以前的指針全都置空,如果再去訪(fǎng)問(wèn)以前的指針就是訪(fǎng)問(wèn)空指針了,這很危險(xiǎn)。所以一般不使用auto_ptr

(2)scoped_ptr

該智能指針底層私有化了拷貝構(gòu)造函數(shù)和operator=賦值函數(shù),

從根本上杜絕了智能指針淺拷貝的發(fā)生,所以scoped_ptr也是不能用在容器當(dāng)中的。

  • 如果容器互相進(jìn)行拷貝或者賦值,就會(huì)引起scoped_ptr對(duì)象的拷貝構(gòu)造和賦值,這是不允許的,代碼會(huì)提示編譯錯(cuò)誤。
  • auto_ptrscoped_ptr這一點(diǎn)上的區(qū)別,有些資料上用所有權(quán)的概念來(lái)描述,道理是相同的。
  • auto_ptr可以任意轉(zhuǎn)移資源的所有權(quán),而scoped_ptr不會(huì)轉(zhuǎn)移所有權(quán)(因?yàn)榭截悩?gòu)造和賦值被禁止了)

一般也不推薦使用scoped_ptr

(3)unique_ptr源碼

	template <class _Dx2 = _Dx, enable_if_t<is_move_constructible_v<_Dx2>, int> = 0>
    unique_ptr(unique_ptr&& _Right) noexcept
        : _Mypair(_One_then_variadic_args_t{}, _STD forward<_Dx>(_Right.get_deleter()), _Right.release()) {}	
	unique_ptr(unique_ptr<_Ty2, _Dx2>&& _Right) noexcept
        : _Mypair(_One_then_variadic_args_t{}, _STD forward<_Dx2>(_Right.get_deleter()), _Right.release()) {}	
	// 拷貝構(gòu)造或者賦值運(yùn)算符的時(shí)候,用于將以前的智能指針置空
	pointer release() noexcept {
        return _STD exchange(_Mypair._Myval2, nullptr);
    }	
	unique_ptr(const unique_ptr&) = delete;
    unique_ptr& operator=(const unique_ptr&) = delete;

從源碼可以看到,unique_ptr直接delete了拷貝構(gòu)造函數(shù)和operator=賦值重載函數(shù)

禁止用戶(hù)對(duì)unique_ptr進(jìn)行顯示的拷貝構(gòu)造和賦值,防止智能指針淺拷貝問(wèn)題的發(fā)生

但是unique_ptr提供了帶右值引用參數(shù)的拷貝構(gòu)造和賦值

即unique_ptr智能指針可以通過(guò)右值引用進(jìn)行拷貝構(gòu)造和賦值操作

    unique_ptr<int> ptr1(new int);
    unique_ptr<int> ptr2(std::move(ptr1));// 使用右值引用的拷貝構(gòu)造,由于執(zhí)行了release,ptr1已經(jīng)被置空
    cout << (ptr1 == nullptr) << endl;    // true
    ptr2 = std::move(ptr1);               // 使用右值引用的operator=賦值重載函數(shù)
    cout << (ptr2 == nullptr) << endl;    // true

用臨時(shí)對(duì)象構(gòu)造新的對(duì)象時(shí),也會(huì)調(diào)用帶右值引用參數(shù)的函數(shù)

unique_ptr<int> get_unique_ptr() {
    unique_ptr<int> tmp(new int);
    return tmp;
}

int main(){
    unique_ptr<int> ptr = get_unique_ptr(); // 調(diào)用帶右值引用參數(shù)的拷貝構(gòu)造函數(shù),由tmp直接構(gòu)造ptr
    return 0;
}

unique_ptr從名字就可以看出來(lái),最終也是只能有一個(gè)智能指針引用資源,其他智能指針全部置空

三、帶引用計(jì)數(shù)的智能指針

#include <iostream>
#include <memory>
using namespace std;

template<typename T>
class RefCnt {
public:
    RefCnt(T* ptr = nullptr){
        mcount = (mptr == nullptr) ? 0 : 1;
    }

    void addRef() {
        mcount++;
    }
    int subRef() {
        return --mcount;
    }
private:
    int mcount;      // mptr指向某個(gè)資源的引用計(jì)數(shù),線(xiàn)程不安全。使用atomic_int線(xiàn)程安全
    T* mptr;         //指向智能指針內(nèi)部指向資源的指針,間接指向資源
};
template<typename T>
class SmartPtr {
public:
    SmartPtr(T* ptr = nullptr) :_ptr(ptr) {
        cout << "SmartPtr()" << endl;
        mpRefCnt = new RefCnt<int>(_ptr); // 用指向資源的指針初始化引用計(jì)數(shù)對(duì)象
    }
    ~SmartPtr() {
        if (0 == mpRefCnt->subRef()) {
            cout << "釋放資源析構(gòu)~SmartPtr()" << endl;
            delete _ptr;
        }
        else {
            cout << "空析構(gòu)~SmartPtr()" << endl;
        }
    }
    const T& operator*() const { return *_ptr; }
    T& operator*() { return *_ptr; }
    const T operator->() const { return _ptr; }
    T operator->() { return _ptr; }
    SmartPtr(const SmartPtr<T>& src) :_ptr(src._ptr), mpRefCnt(src.mpRefCnt) {
        cout << "SmartPtr(const SmartPtr<T>& src)" << endl;
        if (_ptr != nullptr) {
            // 用于拷貝的對(duì)象已經(jīng)引用了資源
            mpRefCnt->addRef();
        }
    }
    SmartPtr<T>& operator=(const SmartPtr<T>& src) {
        cout << "SmartPtr<T>& operator=" << endl;
        if (this == &src) {
            return *this;
        }
        // 當(dāng)前智能指針指向和src相同的資源,考慮是否釋放之前的資源
        if (0 == mpRefCnt->subRef()) {
            // 若之前指向的資源引用計(jì)數(shù)為1,釋放之前的資源
            delete _ptr;
        }
        _ptr = src._ptr;
        mpRefCnt = src.mpRefCnt;
        mpRefCnt->addRef();
        return *this;
    }
private:
    T* _ptr;        // 指向資源的指針
    RefCnt<T>* mpRefCnt; // 指向該資源引用計(jì)數(shù)的指針 
};
int main(){
    SmartPtr<int> ptr1(new int);
    SmartPtr<int> ptr2(ptr1);
    SmartPtr<int> ptr3;
    ptr3 = ptr2;
    *ptr2 = 100;
    cout << *ptr2 << " " << *ptr3 << endl;
    return 0;
}

在這里插入圖片描述

四、shared_ptr 和 weak_ptr

  • shared_ptr:強(qiáng)智能指針,可以改變資源的引用計(jì)數(shù)
  • weak_ptr:弱智能指針,不可改變資源的引用計(jì)數(shù)

weak_ptr -> shared_ptr -> 資源

智能指針的交叉引用問(wèn)題

#include <iostream>
#include <memory>
using namespace std;
class B;
class A {
public:
	A() {
		cout << "A()" << endl;
	}
	~A() {
		cout << "~A()" << endl;
	}
	shared_ptr<B> _ptrb;
};

class B {
public:
	B() {
		cout << "B()" << endl;
	}
	~B() {
		cout << "~B()" << endl;
	}
	shared_ptr<A> _ptra;
};

int main() {
	shared_ptr<A> pa(new A());
	shared_ptr<B> pb(new B());

	pa->_ptrb = pb;
	pb->_ptra = pa;
	cout << pa.use_count() << endl;
	cout << pb.use_count() << endl;
	return 0;
}

/*
A()
B()
2
2
*/

在這里插入圖片描述

解決辦法: 定義對(duì)象時(shí)用shared_ptr,引用對(duì)象時(shí)用weak_ptr

class B;
class A {
public:
	A() {
		cout << "A()" << endl;
	}
	~A() {
		cout << "~A()" << endl;
	}
	weak_ptr<B> _ptrb;
};

class B {
public:
	B() {
		cout << "B()" << endl;
	}
	~B() {
		cout << "~B()" << endl;
	}
	weak_ptr<A> _ptra;
};
/*
A()
B()
1
1
~B()
~A()
*/

weak_ptr只是用于觀(guān)察資源,不能夠使用資源,并沒(méi)有實(shí)現(xiàn)operator*和operator->。可以使用weak_ptr對(duì)象的lock()方法返回shared_ptr對(duì)象,這個(gè)操作會(huì)增加資源的引用計(jì)數(shù)。

五、多線(xiàn)程訪(fǎng)問(wèn)共享對(duì)象的線(xiàn)程安全問(wèn)題

線(xiàn)程A和線(xiàn)程B訪(fǎng)問(wèn)一個(gè)共享對(duì)象,如果線(xiàn)程A已經(jīng)析構(gòu)這個(gè)對(duì)象

線(xiàn)程B又要調(diào)用該共享對(duì)象的成員方法,線(xiàn)程B再去訪(fǎng)問(wèn)該對(duì)象,就會(huì)發(fā)生不可預(yù)期的錯(cuò)誤

#include <iostream>
#include <memory>
#include <thread>
using namespace std;
class A {
public:
	A() {
		cout << "A()" << endl;
	}
	~A() {
		cout << "~A()" << endl;
	}
	void test() {
		cout << "test()" << endl;
	}
};

void handler01(A* q) {
	// 睡眠2s,使得主線(xiàn)程進(jìn)行delete
	std::this_thread::sleep_for(std::chrono::seconds(2));
	q->test();
}
int main() {
	A* p = new A();
	thread t1(handler01, p);
	delete p;
	t1.join();
	return 0;
}

在這里插入圖片描述

在多線(xiàn)程訪(fǎng)問(wèn)共享對(duì)象的時(shí)候,往往需要lock檢測(cè)一下對(duì)象是否存在。

開(kāi)啟一個(gè)新線(xiàn)程,并傳入共享對(duì)象的弱智能指針。

#include <iostream>
#include <memory>
#include <thread>
using namespace std;
class A {
public:
	A() {
		cout << "A()" << endl;
	}
	~A() {
		cout << "~A()" << endl;
	}
	void test() {
		cout << "test()" << endl;
	}
};

void handler01(weak_ptr<A> pw) {
	shared_ptr<A> ps = pw.lock();
	// lock方法判定資源對(duì)象是否析構(gòu)
	if (ps != nullptr) {
		ps->test();
	}else {
		cout << "A對(duì)象已經(jīng)析構(gòu),無(wú)法訪(fǎng)問(wèn)" << endl;
	}
}

int main() {
	{
		shared_ptr<A> p(new A());
		//開(kāi)啟一個(gè)新線(xiàn)程,并傳入共享對(duì)象的弱智能指針
		thread t1(handler01, weak_ptr<A>(p));
		// 將子線(xiàn)程和主線(xiàn)程的關(guān)聯(lián)分離,也就是說(shuō)detach()后子線(xiàn)程在后臺(tái)獨(dú)立繼續(xù)運(yùn)行,
		// 主線(xiàn)程無(wú)法再取得子線(xiàn)程的控制權(quán),即使主線(xiàn)程結(jié)束,子線(xiàn)程未執(zhí)行也不會(huì)結(jié)束。
		t1.detach();
	}
	// 讓主線(xiàn)程等待,給時(shí)間子線(xiàn)程執(zhí)行,否則main函數(shù)最后會(huì)調(diào)用exit方法結(jié)束進(jìn)程
	std::this_thread::sleep_for(std::chrono::seconds(2));
	
	return 0;
}

在這里插入圖片描述

六、自定義刪除器

通常我們使用智能指針管理的資源是堆內(nèi)存,當(dāng)智能指針出作用域的時(shí)候,

在其析構(gòu)函數(shù)中會(huì)delete釋放堆內(nèi)存資源,但是除了堆內(nèi)存資源,智能指針還可以管理其它資源,

比如打開(kāi)的文件,此時(shí)對(duì)于文件指針的關(guān)閉,就不能用delete了,

這時(shí)我們需要自定義智能指針釋放資源的方法。

template<typename T> 
class ArrDeletor {
public:
	// 對(duì)象刪除的時(shí)候需要調(diào)用對(duì)應(yīng)刪除器的()重載函數(shù)
	void operator()(T* ptr) const {
		cout << "ArrDeletor::operator()" << endl;
		delete[] ptr;
	}
};
template<typename T>
class FileDeletor {
public:
	// 對(duì)象刪除的時(shí)候需要調(diào)用對(duì)應(yīng)刪除器的()重載函數(shù)
	void operator()(T* fp) const {
		cout << "FileDeletor::operator()" << endl;
		fclose(fp);
	}
};
int main() {
	unique_ptr<int, ArrDeletor<int>> ptr1(new int[100]);
	unique_ptr<FILE, FileDeletor<FILE>> ptr2(fopen("1.cpp", "w"));
	
	// 使用lambda表達(dá)式
	// function<返回值(參數(shù))> 
	// []叫做捕獲說(shuō)明符,表示一個(gè)lambda表達(dá)式的開(kāi)始。接下來(lái)是參數(shù)列表,即這個(gè)匿名的lambda函數(shù)的參數(shù)
	unique_ptr<int, function<void(int*)>> ptr1(
		new int[100],
		[](int* p)->void {
			cout << "call lambda release new int[]" << endl;
			delete[] p;
		}
	);
	unique_ptr<FILE, function<void(FILE*)>>  ptr2(
		fopen("1.cpp", "w"),
		[](FILE* p)->void {
			cout << "call lambda release fopen(\"1.cpp\", \"w\")" << endl;
			fclose(p);
		}
	);
	return 0;
}

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

相關(guān)文章

  • 在C++中反射調(diào)用.NET的方法(三)

    在C++中反射調(diào)用.NET的方法(三)

    在.NET與C++之間傳輸集合數(shù)據(jù)的方法是怎么樣的呢?接下來(lái)通過(guò)本文給大家分享在C++中反射調(diào)用.NET(三),需要的朋友參考下吧
    2017-02-02
  • C語(yǔ)言約瑟夫環(huán)的實(shí)現(xiàn)

    C語(yǔ)言約瑟夫環(huán)的實(shí)現(xiàn)

    這篇文章主要介紹了C語(yǔ)言約瑟夫環(huán)的實(shí)現(xiàn)的相關(guān)資料,這里主要是利用數(shù)據(jù)數(shù)據(jù)結(jié)果中循環(huán)鏈表來(lái)實(shí)現(xiàn),需要的朋友可以參考下
    2017-08-08
  • Qt TCP實(shí)現(xiàn)簡(jiǎn)單通信功能

    Qt TCP實(shí)現(xiàn)簡(jiǎn)單通信功能

    這篇文章主要為大家詳細(xì)介紹了Qt TCP實(shí)現(xiàn)簡(jiǎn)單通信功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • C++11 上下文關(guān)鍵字的具體實(shí)踐

    C++11 上下文關(guān)鍵字的具體實(shí)踐

    本文主要介紹了C++11 上下文關(guān)鍵字的具體實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • C++詳解使用floor&ceil&round實(shí)現(xiàn)保留小數(shù)點(diǎn)后兩位

    C++詳解使用floor&ceil&round實(shí)現(xiàn)保留小數(shù)點(diǎn)后兩位

    這篇文章主要介紹了C++使用floor&ceil&round實(shí)現(xiàn)保留小數(shù)點(diǎn)后兩位的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • VC實(shí)現(xiàn)屏幕截詞功能的方法詳解

    VC實(shí)現(xiàn)屏幕截詞功能的方法詳解

    這篇文章主要介紹了VC實(shí)現(xiàn)屏幕截詞功能的方法詳解,對(duì)于深入的理解windows程序運(yùn)行原理很有幫助,需要的朋友可以參考下
    2014-07-07
  • C語(yǔ)言詳解冒泡排序?qū)崿F(xiàn)

    C語(yǔ)言詳解冒泡排序?qū)崿F(xiàn)

    冒泡排序是一種簡(jiǎn)單的排序算法,它也是一種穩(wěn)定排序算法。其實(shí)現(xiàn)原理是重復(fù)掃描待排序序列,并比較每一對(duì)相鄰的元素,當(dāng)該對(duì)元素順序不正確時(shí)進(jìn)行交換。一直重復(fù)這個(gè)過(guò)程,直到?jīng)]有任何兩個(gè)相鄰元素可以交換,就表明完成了排序
    2022-04-04
  • C++實(shí)現(xiàn) 單例模式實(shí)例詳解

    C++實(shí)現(xiàn) 單例模式實(shí)例詳解

    這篇文章主要介紹了C++實(shí)現(xiàn) 單例模式實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • C語(yǔ)言實(shí)現(xiàn)自動(dòng)發(fā)牌程序

    C語(yǔ)言實(shí)現(xiàn)自動(dòng)發(fā)牌程序

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)自動(dòng)發(fā)牌程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • C++中浮點(diǎn)數(shù)、double類(lèi)型如何與0值作比較詳解

    C++中浮點(diǎn)數(shù)、double類(lèi)型如何與0值作比較詳解

    浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)機(jī)制和整型數(shù)不同,其有舍入誤差,在計(jì)算機(jī)中用近似表示任意某個(gè)實(shí)數(shù),這篇文章主要介紹了C++中浮點(diǎn)數(shù)、double類(lèi)型如何與0值作比較的相關(guān)資料,需要的朋友可以參考下
    2025-03-03

最新評(píng)論

平远县| 崇信县| 连城县| 新和县| 建湖县| 永泰县| 钟祥市| 内黄县| 十堰市| 平果县| 灌阳县| 浠水县| 万载县| 武宁县| 南安市| 当涂县| 义马市| 肇东市| 高阳县| 丹寨县| 邓州市| 桦甸市| 林芝县| 平昌县| 宁都县| 鄂温| 屏东县| 晋宁县| 四川省| 多伦县| 安乡县| 寻甸| 醴陵市| 黄大仙区| 平乡县| 新泰市| 土默特左旗| 泗水县| 广汉市| 金塔县| 永年县|