Teknologian jakaminen

Koe älykkäiden osoittimien voima

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina


Kurssiluettelo



1. Perustiedot älykkäistä osoittimista

Raaka-osoittimien haitat:

  1. unohtaadeleteVapauta resurssit aiheuttaen resurssivuotoja
  2. olla olemassadeleteOhjelma poistui normaalisti ennen (esimifkeskelläreturn) tai poistu epätavallisesti ennen kuin on liian myöhäistädelete, mikä johtaa resurssien vuotamiseen
  3. Sama resurssi vapautetaan useita kertoja, jolloin villit osoittimet vapautuvat ja ohjelma kaatuu.

Tällä hetkellä tarvitaan älykkäitä vihjeitä. Älykkäiden osoittimien älykkyys näkyy pääasiassa siinä, että käyttäjien ei tarvitse kiinnittää huomiota resurssien vapauttamiseen, koska älykkäät osoittimet auttavat hallitsemaan resurssien vapauttamista täysin. Se varmistaa, että ohjelman logiikka toimii riippumatta , se suoritetaan normaalisti tai luo poikkeuksia.Kun resurssit vanhenevat (toiminnon laajuus tai ohjelma loppuu), ne vapautetaan.

Toteutetaan yksinkertainen älykäs osoitin itse

template<typename T>
class CSmartPtr
{
public:
	CSmartPtr(T* ptr = nullptr) : mptr(ptr) {}
	~CSmartPtr() { delete mptr; }
private:
	T* mptr;
};

int main()
{
	CSmartPtr<int> ptr(new int());
	/*其它的代码...*/

	/*
	由于ptr是栈上的智能指针对象,不管是函数正常执行完,
	还是运行过程中出现异常,栈上的对象都会自动调用析构函数,
	在析构函数中进行了delete操作,保证释放资源
	*/

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  1. Älykkäät osoittimet sisältyvät raakaosoittimien oliosuuntautuneeseen kapselointiin, resurssien osoitteiden alustamiseen rakentajassa ja resurssien vapauttamiseen destruktorissa.
  2. Hyödyntämällä pinossa olevien objektien automaattisen laajuuden tuhoamisen ominaisuutta, resurssit taataan vapautuvan älykäs osoittimen tuhoajassa.

Siksi edellä mainituista ominaisuuksista johtuen älykkäät osoittimet määritellään yleensä pinossa. samaan aikaan,Älykäs osoitin on luokkaobjekti , osoitin välitetään tämän luokan konstruktorissa ja hyväksytty osoitin vapautetaan destruktorissa.Koska tämän tyyppinen objekti varataan ja vapautetaan pinosta, se vapautetaan automaattisesti, kun toimintomme (tai ohjelmamme) päättyy.

Joten voidaanko älykkäitä osoittimia määrittää kasaan?esimerkiksiCSmartPtr* p = new CSmartPtr(new int);, kokoelma voi kulkea, mutta määritelmä täälläpVaikka se on älykäs osoitintyyppi, se on pohjimmiltaan raakaosoitinpSe on silti tehtävä manuaalisestidelete, se on palannut ongelmaan, jonka kohtasimme alussa paljailla osoittimilla, joten älä käytä sitä näin

Tietysti älykkäiden osoittimien tulee olla samanlaisia ​​kuin raakaosoittimia, ja niissä on myös oltava raakaosoittimien yhteisiä ominaisuuksia.*ja->Kahden operaattorin ylikuormitetut toiminnot ovat todella samoja kuin raakaosoittimet, kun niitä käytetään.

template<typename T>
class CSmartPtr
{
public:
	CSmartPtr(T* ptr = nullptr) : mptr(ptr) {}
	~CSmartPtr() { delete mptr; }

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

private:
	T* mptr;
};


int main()
{
	CSmartPtr<int> ptr(new int());

	*ptr = 20;	// operator*()一定要返回引用,这样才可以赋值

	cout << *ptr << endl;	// 20

	class Test
	{
	public:
		void test() { cout << "call Test::test()" << endl; }
	};

	CSmartPtr<Test> ptr2 = new Test();

	(*ptr2).test();	// (*ptr2)取出Test对象,用对象调用方法

	// operator->()返回的是一个指针,实现了用指针调用函数
	// 即(ptr2.operator->()) -> test()
	ptr2->test();

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

2. Älykkäät osoittimet ilman viitelaskentaa

Edellisessä osiossa toteutettu älykäs osoitin on hyvin samanlainen kuin tavallinen käytössä oleva raakaosoitin, mutta siinä on silti suuria ongelmia. Katso seuraava koodi:

CSmartPtr<int> p1(new int());
CSmartPtr<int> p2(p1);	// 拷贝构造
  • 1
  • 2

Käynnissä oleva koodi kaatuu suoraan, koska oletuskopion rakentaja tekee matalan kopion.p1jap2pitää samaanew intresurssi,p2Tuhoaminen vapauttaa ensin resurssit, sittenp1Kun tuhoutuu, siitä tuleedeleteVilli osoitin, ohjelma kaatuu

Joten kuinka ratkaista pinnallisen kopioinnin aiheuttamat ongelmat?kirjoittaa uudelleenCSmartPtrKatso kopiokonstruktori

CSmartPtr(const CSmartPtr<T>& src) { mptr = new T(*src.mptr); }
  • 1

Nyt koodi toimii kuitenkin normaalisti tässä vaiheessap1jap2Hallitaan kahta eri resurssia, jos käyttäjät eivät ymmärrä niitä, he luulevat virheellisesti niinp1jap2Hallinnoi samaa resurssia

Siksi on väärin kirjoittaa kopiokonstruktori tällä tavalla

Joten kuinka ratkaista älykkäiden osoittimien matala kopioongelma? Kaksi tapaa:Älykkäät osoittimet ilman viitelaskentaa, älykkäät osoittimet viitelaskennan kanssa

Katsotaanpa ensin tämä osioÄlykkäät osoittimet ilman viitteiden laskemista

  1. auto_ptr(C++98, nyt vanhentunut)
  2. scoped_ptr(Boost kirjasto)
  3. unique_ptr(C++11, suositeltava)

Sisällytä otsikkotiedosto, kun käytät:#include <memory>

auto_ptr

Mietitäänpä tätä koodia ensin

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

*ptr2 = 20;
cout << *ptr1 << endl;
  • 1
  • 2
  • 3
  • 4
  • 5

Operaatio kaatui.auto_ptrlähdekoodi

Lisää kuvan kuvaus tähän

Voit nähdä, että kopiorakenne soittaa saapuvalle objektillereleasemenetelmä, tämä menetelmä on laittaa_RightOsoitettu resurssi palautetaan uuteenauto_ptrkohde, ja samalla_Right(vanhaauto_ptr)/_Myptrasetettunullptr

Lyhyesti sanottuna se on laittaa vanhaauto_ptrOsoitettu resurssi annetaan uudelleauto_ptrPidetty, vanha on asetettunullptr .Siksi yllä oleva koodi käyttää*ptr1Vain väärin.auto_ptrAnna aina viimeisen älykäs osoittimen hallita resurssia)

Niin,auto_ptrVoidaanko sitä käyttää säiliössä. Katso seuraava koodi.

int main()
{
	vector<auto_ptr<int>> vec;
	vec.push_back(auto_ptr<int>(new int(10)));
	vec.push_back(auto_ptr<int>(new int(20)));
	vec.push_back(auto_ptr<int>(new int(30)));
	
	cout << *vec[0] << endl;	// 10
	
	vector<auto_ptr<int>> vec2 = vec;
	/* 这里由于上面做了vector容器的拷贝,
	相当于容器中的每一个元素都进行了拷贝构造,
	原来vec中的智能指针全部为nullptr了,
	再次访问就成访问空指针了,程序崩溃
	*/
	
	cout << *vec[0] << endl;	// 程序崩溃
	
	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

Siksi se on vanhentunut C++:ssaauto_ptr , ellei sovellusskenaario ole hyvin yksinkertainen.jaauto_ptrVanhentunut C++11:ssä ja poistettu kokonaan C++17:stä

Yhteenveto:auto_ptrÄlykkäissä osoittimissa ei ole viitelaskentaa, joten ne ratkaisevat matalan kopioinnin ongelman kopioimalla suoraan edellisenauto_ptron asetettunullptr, olkoon vain viimeinenauto_ptrpitää resursseja

Scoped_ptr

Sinun on asennettava Boost-kirjasto, joka sisältää otsikkotiedostot.#include <boost/scoped_ptr.hpp>Valmis käytettäväksi

Katsoscoped_ptrLähdekoodi:

template<class T> class scoped_ptr
{
private:
	scoped_ptr(scoped_ptr const&);
	scoped_ptr& operator=(scoped_ptr const&);
...
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

voidaan nähdäscoped_ptrKopioiden rakentaja ja määritystoiminto ovat molemmat yksityistetty, joten objekti ei tue näitä kahta toimintoa eikä sitä voida kutsua, mikä estää pohjimmiltaan matalien kopioiden esiintymisen.

niinscoped_ptrSitä ei voi käyttää säilöissä, jos säiliöt kopioivat tai määrittävät arvoja toisilleenscoped_ptrObjektikopion rakentaja ja osoitusfunktio, käännösvirhe

scoped_ptrjaauto_ptrEro: Voidaan selittää omistajuudella,auto_ptrResurssien omistusoikeus voidaan siirtää haluttaessascoped_ptrOmistajuutta ei siirretä (koska kopiointikonstruktorit ja määritystoiminnot on poistettu käytöstä)

scoped_ptrYleensä ei käytetä

ainutlaatuinen_ptr

Katso ensinunique_ptrOsa lähdekoodia:

template<class _Ty, class _Dx>
class unique_ptr
{
public:
	/*提供了右值引用的拷贝构造函数*/
	unique_ptr(unique_ptr&& _Right) { ... }
	
	/*提供了右值引用的operator=赋值重载函数*/
	unique_ptr& operator=(unique_ptr&& _Right) { ... }

	/*
	删除了unique_ptr的拷贝构造和赋值函数,
	因此不能做unique_ptr智能指针对象的拷贝构造和赋值,
	防止浅拷贝的发生
	*/
	unique_ptr(const unique_ptr&) = delete;
	unique_ptr& operator=(const unique_ptr&) = delete;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Ylhäältä katsottuna,unique_ptrhiemanscoped_ptrSamoin tehdään, eli kopioinnin rakentaminen ja osoitus ylikuormitetut toiminnot poistetaan käytöstä ja käyttäjiä ei saa käyttääunique_ptrSuorita eksplisiittinen kopion rakentaminen ja määrittäminen estääksesi älykäs osoittimen matalia kopiointiongelmia.

Koska kopiokonstruktoria ei ole,unique_ptr<int> p1(new int()); unique_ptr<int> p2(p1);Luonnollisesti se on väärin

muttaunique_ptrTarjoaa kopiointikonstruktorit ja osoitusfunktiot rvalue-viittausparametreilla, eliunique_ptrÄlykkäät osoittimet voivat suorittaa kopiointi- ja määritystoimintoja rarvoviittausten kautta tai luodaunique_ptrPaikka väliaikaisille esineille, esimunique_ptrToiminnon palautusarvona esimerkkikoodi on seuraava:

// 示例1
unique_ptr<int> p1(new int());
unique_ptr<int> p2(move(p1));	// 使用了右值引用的拷贝构造
p2 = move(p1);	// 使用了右值引用的operator=赋值重载函数
  • 1
  • 2
  • 3
  • 4
// 示例2
unique_ptr<int> test_uniqueptr()
{
	unique_ptr<int> ptr(new int());
	return ptr;
}
int main()
{
	/*
	此处调用test_uniqueptr函数,在return ptr代码处,
	调用右值引用的拷贝构造和赋值函数
	*/
	unique_ptr<int> p = test_uniqueptr();	// 调用带右值引用的拷贝构造函数
	p = test_uniqueptr();	// 调用带右值引用的operator=赋值重载函数
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Käytä sittenunique_ptrEtuna on, että käyttäjät voivat käyttää jotain vastaavaaunique_ptr<int> p2(move(p1));lausunto, sen voi selvästi nähdäp1Resurssit siirretty osoitteeseenp2p1Enempää resursseja ei säilytetä, jos käytätauto_ptrSe ei tule olemaan nimenomaisestimoveKirjoitettu, tarkoitus ei ole ilmeinen, jos et ymmärrä alla olevaa kerrosta, käytät sitä väärin.

Samaan aikaan alkaenunique_ptrKuten nimestä näkyy, resurssiin viittaava älykäs osoitin voi olla lopussa. Siksi on suositeltavaa antaa etusijalle älykäs osoitin ilman viitelaskentaa.unique_ptr

3. Älykkäät osoittimet viitelaskentaan

Älykkäät osoittimet viitelaskentaan sisältävät pääasiassashared_ptrjaweak_ptr, viitelaskentaanhyötyäToisin sanoen useat älykkäät osoittimet voivat hallita samaa resurssia.

Viitelaskennan kanssa: Yhdistä kunkin objektin resurssin viitemäärä.

Kun useiden älykkäiden osoittimien sallitaan osoittaa samaan resurssiin, jokainen älykäs osoitin lisää 1:n resurssin viitemäärään. Viimeinen älykäs osoitin Kun resurssin viitemäärä laskee 1:stä nollaan, se tarkoittaa, että resurssi voidaan vapauttaa Viimeisen älykäs osoittimen tuhoaja käsittelee resurssin vapauttamisen.

  • Kun viitettä lasketaanmiinus 1 ei ole 0Kun , nykyinen älykäs osoitin ei enää käytä tätä resurssia, mutta on olemassa muita älykkäitä osoittimia, jotka käyttävät tätä resurssia Nykyinen älykäs osoitin ei voi tuhota tätä resurssia, vaan se voi mennä vain suoraan
  • Kun viitettä lasketaanPienennä 1 arvoon 0Milloin, se tarkoittaa, että nykyinen älykäs osoitin on viimeinen älykäs osoitin, joka käyttää tätä resurssia, joten se on vastuussa tämän resurssin vapauttamisesta.

Simuloi älykkään osoittimen käyttöönottoa viitelaskennan avulla

Ota se, mikä on toteutettu aiemminCSmartPtrTee muutokset ja lataa koodi suoraan:

// 对资源进行引用计数的类
template<typename T>
class RefCnt
{
public:
	RefCnt(T* ptr = nullptr) : mptr(ptr)
	{
		if (mptr != nullptr)
			mcount = 1;
	}
	void addRef() { mcount++; }	// 增加资源的引用计数
	int delRef() { return --mcount; }

private:
	T* mptr;
	int mcount;
};

template<typename T>
class CSmartPtr
{
public:
	CSmartPtr(T* ptr = nullptr) : mptr(ptr)
	{
		// 智能指针构造的时候给资源建立引用计数对象
		mpRefCnt = new RefCnt<T>(mptr);
	}
	~CSmartPtr()
	{
		if (0 == mpRefCnt->delRef())
		{
			delete mptr;
			mptr = nullptr;
		}
	}

	// 实现拷贝构造
	CSmartPtr(const CSmartPtr<T>& src)
		:mptr(src.mptr), mpRefCnt(src.mpRefCnt)
	{
		if (mptr != nullptr)
			mpRefCnt->addRef();
	}
	CSmartPtr<T>& operator=(const CSmartPtr<T>& src)
	{
		// 防止自赋值
		if (this == &src)
			return *this;

		// 本身指向的资源减1
		// 如果减1为0释放资源;如果减1不为0直接走
		if (0 == mpRefCnt->delRef()) { delete mptr; }

		mptr = src.mptr;
		mpRefCnt = src.mpRefCnt;
		mpRefCnt->addRef();
		return *this;
	}

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

private:
	T* mptr;	// 指向资源的指针
	RefCnt<T>* mpRefCnt;	// 指向该资源引用计数对象的指针
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
// 那么现在就不会报错了,不会对同一个资源释放多次
CSmartPtr<int> ptr1(new int());
CSmartPtr<int> ptr2(ptr1);
CSmartPtr<int> ptr3;
ptr3 = ptr2;

*ptr1 = 20;
cout << *ptr2 << " " << *ptr3 << endl;	// 20 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Näin useat älykkäät osoittimet voivat hallita samaa resurssia.

Nyt käyttöönottamamme älykkäät osoittimet eivät kuitenkaan ole säikeen turvallisia, eikä niitä voida käyttää monisäikeisissä skenaarioissa.Toteuttaja Curryshared_ptrjaweak_ptrSe on lankaturvallinen!

share_ptr ristiviittausongelma

  • shared_ptrvoimakasÄlykkäät osoittimet (voi muuttaa resurssien viitemäärää)
  • weak_ptrheikkoÄlykkäät osoittimet (älä muuta resurssin viitemäärää)

Mitä toteutimme edellisessä osiossaCSmartPtrMyös vahva älykäs osoitin (voi muuttaa resurssin viitemäärää)

Sen voi ymmärtää näin: Heikko älykäs osoitin tarkkailla Vahvat älykkäät osoittimet Vahvat älykkäät osoittimettarkkailla Resurssit (muisti)

Niin,Vahva älykäs osoittimen ristiviittaus (ympyräviittaus) ongelma mitä se sitten on?Tule katsomaan

class B; // 前置声明类B
class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
	shared_ptr<B> _ptrb;	// 指向B对象的智能指针
};
class B
{
public:
	B() { cout << "B()" << endl; }
	~B() { cout << "~B()" << endl; }
	shared_ptr<A> _ptra;	// 指向A对象的智能指针
};

int main()
{
	shared_ptr<A> pa(new A());	// pa指向A对象,A的引用计数为1
	shared_ptr<B> pb(new B());	// pb指向B对象,B的引用计数为1
	pa->_ptrb = pb;	// A对象的成员变量_ptrb也指向B对象,B的引用计数为2
	pb->_ptra = pa;	// B对象的成员变量_ptra也指向A对象,A的引用计数为2

	cout << pa.use_count() << endl;
	cout << pb.use_count() << endl;

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

operaation tulos:

A()
B()
2
2
  • 1
  • 2
  • 3
  • 4

Kuten näet, A ja B eivät tuhoudu, mikä tarkoittaa, että ristiviittaukset aiheuttavatnewUlostulevia resursseja ei voida vapauttaa, mikä johtaa resurssien vuotamiseen!

analysoida:
Lisää kuvan kuvaus tähänLisää kuvan kuvaus tähän
ulosmaintoiminnon laajuus,pajapbKaksi paikallista objektia tuhotaan ja objektin A ja B:n viitemäärät pienennetään 2:sta 1:een. A:n ja B:n vapauttamisen ehtoja ei voida täyttää (vapauttamisen ehto on, että A:n ja B:n viitemäärää pienennetään). 0), mikä aiheuttaa kaksinewA- ja B-objekteja, jotka tulivat ulos, ei voida vapauttaa, mikä aiheuttaa muistivuotojaVahva älykäs osoittimen ristiviittaus (ympyräviittaus) ongelma

Ratkaisu: Käytä vahvoja älykkäitä osoittimia, kun määrität objekteja, ja käytä heikkoja älykkäitä osoittimia viitattaessasi objekteihin.

class B; // 前置声明类B
class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
	weak_ptr<B> _ptrb;	// 指向B对象的弱智能指针(引用对象时,用弱智能指针)
};
class B
{
public:
	B() { cout << "B()" << endl; }
	~B() { cout << "~B()" << endl; }
	weak_ptr<A> _ptra;	// 指向A对象的弱智能指针(引用对象时,用弱智能指针)
};


int main()
{
	// 定义对象时,用强智能指针
	shared_ptr<A> pa(new A());	// pa指向A对象,A的引用计数为1
	shared_ptr<B> pb(new B());	// pb指向B对象,B的引用计数为1

	// A对象的成员变量_ptrb也指向B对象,B的引用计数为1,因为是弱智能指针,引用计数没有改变
	pa->_ptrb = pb;
	// B对象的成员变量_ptra也指向A对象,A的引用计数为1,因为是弱智能指针,引用计数没有改变
	pb->_ptra = pa;

	cout << pa.use_count() << endl; // 1
	cout << pb.use_count() << endl; // 1

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

operaation tulos:

A()
B()
1
1
~B()
~A()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

voidaan nähdä, ulosmaintoiminnon laajuus,pajapbKaksi paikallista objektia tuhotaan, ja objektin A ja objektin B vertailumäärät pienennetään 1:stä 0:aan saavuttaen ehdot A:n ja B:n vapauttamiselle.newA-objekti ja B-objekti, jotka tulivat ulos, tuhoutuivat, mikä ratkaisi ongelman.Vahva älykäs osoittimen ristiviittaus (ympyräviittaus) ongelma

Lisää kuvan kuvaus tähän
voidaan nähdä,weak_ptrHeikko älykäs osoitin ei muuta resurssin viitemäärää, mikä tarkoittaa, että heikko älykäs osoitin vain tarkkailee, onko objekti elossa (onko viiteluku 0), eikä voi käyttää resurssia.

Sitten jos tällä kertaa, lisää luokkaan Avoid testA() { cout << "非常好用的方法!!" << endl; }Tällainen menetelmä lisää B:tävoid func() { _ptra->testA(); }Onko oikein kutsua tätä menetelmää?

  • Ei voi, koska heikko älykäs osoitin on vain tarkkailija eikä voi käyttää resursseja, eli se ei tarjoa*ja->Operaattoreiden ylikuormitetut toiminnot eivät voi käyttää raakaosoittimien kaltaisia ​​toimintoja

Ratkaisu:

class B; // 前置声明类B
class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
	void testA() { cout << "非常好用的方法!!" << endl; }
	weak_ptr<B> _ptrb;	// 指向B对象的弱智能指针(引用对象时,用弱智能指针)
};
class B
{
public:
	B() { cout << "B()" << endl; }
	~B() { cout << "~B()" << endl; }
	void func()
	{
		shared_ptr<A> sp = _ptra.lock(); // 提升方法,出函数作用域就自动析构了
		if (sp != nullptr)
			sp->testA();
	}
	weak_ptr<A> _ptra;	// 指向A对象的弱智能指针(引用对象时,用弱智能指针)
};

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; // 1
	cout << pb.use_count() << endl; // 1

	pb->func();

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

Siitä huolimatta weak_ptrei omista esinettä, mutta se voi kulkealock()menetelmä yrittää saada osoittimen objektiinshared_ptr, jos esine on vielä elossa (eli muitashared_ptrosoita sitä),lock()palauttaa osoittimen objektiinshared_ptrMuussa tapauksessa palautetaan tyhjä arvoshared_ptr

käyttäälock()Tyypillinen skenaario menetelmille on, kun tilapäinen pääsy vaaditaanweak_ptrkohde osoitti, eivätkä samalla halua lisätä kohteen käyttöikää

operaation tulos:

A()
B()
1
1
非常好用的方法!!
~B()
~A()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Tässä vaiheessa näet, että sitä kutsuttiin oikein!

4. Säikeen turvallisuusongelmat, kun useat säikeet käyttävät jaettuja objekteja

Katsotaanpa yksiSäikeen turvallisuusongelmat käytettäessä jaettuja objekteja useista säikeistä: Säie A ja säie B käyttävät jaettua objektia, jos säie A tuhoaa objektin, säie A on saattanut tuhota objektin, mutta säie B ei Jos yrität käyttää objektia, tapahtuu odottamaton virhe.

Katso ensin seuraava koodi:

class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
	void testA() { cout << "非常好用的方法!!" << endl; }
};

// 子线程
void handler01(A* q)
{
	// 睡眠两秒,此时main主线程已经把A对象给delete析构掉了
	std::this_thread::sleep_for(std::chrono::seconds(2));
	q->testA();
}

// main线程
int main()
{
	A* p = new A();
	thread t1(handler01, p);
	delete p;
	//阻塞当前线程,直到调用join()的线程结束
	t1.join();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

toteutuksessaq->testA();Kun tämä lausunto annetaan,mainSäie on tuhonnut jaetun objektin, mikä on ilmeisen kohtuutonta.

Jos haluat ohittaaqJos osoitin haluaa käyttää A-objektia, sen on määritettävä, onko A-objekti elossa. Jos A-objekti on elossa, kutsutestAMenetelmässä ei ole ongelmaa, jos A-objekti on tuhoutunuttestA on ongelma!TarkoittaenqKun käytät objektia A, sinun on havaittava, onko objekti A elossa. Kuinka ratkaista tämä ongelma Sinun on käytettävä vahvoja ja heikkoja älykkäitä viitteitä.

Katsotaanpa tätä koodia:

// 子线程
void handler01(weak_ptr<A> wp)
{
	// 睡眠两秒
	std::this_thread::sleep_for(std::chrono::seconds(2));
	shared_ptr<A> sp = wp.lock();
	if (sp != nullptr)
		sp->testA();
	else
		cout << "A对象已经析构,不能再访问!" << endl;
}

// main线程
int main()
{
	shared_ptr<A> p(new A());
	thread t1(handler01, weak_ptr<A>(p));
	t1.join();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

operaation tulos:

A()
非常好用的方法!!
~A()
  • 1
  • 2
  • 3

Voit nähdä, että se on käynnissäsp->testA();,koskamainLanka kutsuit1.join()Menetelmä odottaa aliketjun päättymistä tällä hetkelläwpkulkealockOnnistuneesti ylennettysp

Muokkaa yllä olevaa koodia:

// 子线程
void handler01(weak_ptr<A> wp)
{
	// 睡眠两秒
	std::this_thread::sleep_for(std::chrono::seconds(2));
	shared_ptr<A> sp = wp.lock();
	if (sp != nullptr)
		sp->testA();
	else
		cout << "A对象已经析构,不能再访问!" << endl;
}

// main线程
int main()
{
	{
		shared_ptr<A> p(new A());
		thread t1(handler01, weak_ptr<A>(p));
		t1.detach();
	}
	std::this_thread::sleep_for(std::chrono::seconds(3));
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

operaation tulos:

A()
~A()
A对象已经析构,不能再访问!
  • 1
  • 2
  • 3

Kuten näet, asetamme laajuuden ja myös asetimmet1Erota viestiketjut käyttämällä älykkäitä osoittimiapKun A tuhotaan soveltamisalan ulkopuolella, se tulostetaan tällä hetkelläA对象已经析构,不能再访问!, tuo onwpkulkealockPäivitys tiedostoon epäonnistuisp

Yllä oleva on säikeen turvallisuusongelma, kun useat säikeet käyttävät jaettuja objektejashared_ptrjaweak_ptrTyypillinen sovellus.

5. Älykäs osoittimen poistaja

6. On suositeltavaa käyttää make_shared-parametria share_ptr:n sijaan


Viiteartikkeli:C++-älyosoittimien syvällinen ymmärtäminen