प्रौद्योगिकी साझेदारी

स्मार्ट सूचकानां शक्तिं अनुभवन्तु

2024-07-12

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


पाठ्यक्रमसूची



1. स्मार्ट सूचकानाम् मूलभूतं ज्ञानम्

कच्चे सूचकानाम् दोषाः : १.

  1. विस्मृतdeleteसंसाधनं मुक्तं कुर्वन्तु, संसाधनस्य लीकेजं जनयति
  2. अस्तिdeleteकार्यक्रमः सामान्यतया पूर्वं निर्गतवान् (उदाहरणार्थं, यदिifमध्यंreturn) अथवा विलम्बस्य पूर्वं असामान्यतया निर्गन्तुं शक्नुवन्तिdelete, संसाधनस्य लीकेजं जनयति
  3. एकमेव संसाधनं बहुवारं मुक्तं भवति, येन वन्यसूचकाः मुक्ताः भवन्ति, कार्यक्रमः च दुर्घटना भवति ।

अस्मिन् समये स्मार्ट-पॉइण्टर्-इत्यस्य आवश्यकता वर्तते । स्मार्ट-सूचकानाम् शब्दबुद्धिः मुख्यतया अस्मिन् तथ्ये प्रतिबिम्बिता यत् उपयोक्तृभ्यः संसाधनानाम् विमोचनं प्रति ध्यानं दातुं आवश्यकता नास्ति, यतः स्मार्ट-सूचकाः संसाधनानाम् विमोचनं पूर्णतया प्रबन्धयितुं भवन्तं साहाय्यं करिष्यन्ति एतत् सुनिश्चितं करिष्यति यत् कार्यक्रमस्य तर्कः कथं अपि चाल्यते , सामान्यतया निष्पादयिष्यति अपवादं जनयिष्यति वा ।यदा संसाधनानाम् अवधिः समाप्तः भवति (function scope अथवा program end), तदा ते मुक्ताः भविष्यन्ति ।

वयं स्वयमेव सरलं स्मार्ट-सूचकं कार्यान्वयामः

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. स्मार्ट-सूचकाः कच्चे-सूचकानाम् वस्तु-उन्मुख-समायोजने, कन्स्ट्रक्टर्-मध्ये संसाधन-सङ्केतानां आरम्भ-करणे, विध्वंसके संसाधन-विमोचने च समाहिताः भवन्ति
  2. स्तम्भे वस्तुनां स्वचालितव्याप्तिविनाशस्य विशेषतायाः उपयोगेन स्मार्टसूचकस्य विध्वंसके संसाधनानाम् मुक्तिः गारण्टी भवति

अतः उपर्युक्तलक्षणानाम् कारणात् सामान्यतया स्मार्ट-सूचकाः स्टैक्-उपरि परिभाषिताः भवन्ति । तस्मिन् एव काले, २.स्मार्ट सूचकः एकः वर्गस्य वस्तु अस्ति , अस्य वर्गस्य कन्स्ट्रक्टर् मध्ये एकः सूचकः पारितः भवति, पारितः सूचकः च विध्वंसके मुक्तः भवति ।यतः एषः प्रकारः ऑब्जेक्ट् आवंटितः भवति, स्टैक् इत्यत्र मुक्तः च भवति, अतः अस्माकं फंक्शन् (अथवा प्रोग्राम्) समाप्ते सति स्वयमेव मुक्तः भविष्यति ।

अतः, स्मार्ट-सूचकाः राशेः उपरि परिभाषितुं शक्यन्ते वा ?उदाहरणतयाCSmartPtr* p = new CSmartPtr(new int);, संकलनं गन्तुं शक्नोति, परन्तु अत्र परिभाषाpयद्यपि स्मार्ट सूचकप्रकारः अस्ति तथापि मूलतः कच्चा सूचकः अस्ति, अतःpअद्यापि हस्तचलितरूपेण कर्तुं आवश्यकता अस्तिdelete, इदं पुनः आरम्भे वयं नग्नसूचकैः सह सम्मुखीकृतवन्तः समस्यायाः कृते अस्ति, अतः एतादृशं उपयोगं मा कुर्वन्तु

अवश्यं, स्मार्ट-सूचकाः कच्चा-सूचकानाम् सदृशाः भवेयुः, तेषु कच्च-सूचकानाम् सामान्य-विशेषताः अपि प्रदातव्याः ।*तथा->द्वयोः ऑपरेटर्योः अतिभारितकार्यं यथार्थतया कच्चा सूचकानाम् समानं भवति यदा उपयोगः भवति ।

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. सन्दर्भगणना विना स्मार्ट सूचकाः

पूर्वभागे कार्यान्वितः स्मार्ट-सूचकः प्रयुक्तस्य साधारण-कच्चा-सूचकस्य बहु सदृशः अस्ति, परन्तु अद्यापि तस्य महती समस्याः सन्ति निम्नलिखित-सङ्केतं पश्यन्तु ।

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

चालितः कोडः प्रत्यक्षतया क्रैश भवति यतः पूर्वनिर्धारितः प्रतिलिपिनिर्माता अतल्लीनप्रतिलिपिं करोति ।p1तथाp2समानं धारयतिnew intसंसाधनम्, २.p2विनाशः प्रथमं संसाधनं मुञ्चति, ततःp1नष्टे सति भवतिdeleteवन्यसूचकः, कार्यक्रमः दुर्घटना भवति

अतः, अतल्लीनप्रतिलिपिना उत्पद्यमानानां समस्यानां समाधानं कथं करणीयम् ?पुनर्लेखनम्CSmartPtrप्रतिलिपिनिर्मातारं पश्यन्तु

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

इदानीं कोडः सामान्यतया चाल्यते तथापि अस्मिन् बिन्दौp1तथाp2द्वौ भिन्नौ संसाधनौ प्रबन्धितौ भवतः यदि उपयोक्तारः तान् न अवगच्छन्ति तर्हि ते भूलवशं तत् चिन्तयिष्यन्तिp1तथाp2समानं संसाधनं प्रबन्धयन्तु

अतः अस्माकं कृते प्रतिलिपिनिर्मातृम् एवं लिखितुं दोषः

अतः, स्मार्ट-सूचकानाम् उथल-प्रति-समस्यायाः समाधानं कथं करणीयम्? द्वे विधिः- १.सन्दर्भगणना विना स्मार्ट सूचकाः, सन्दर्भगणना सह स्मार्ट सूचकाः

प्रथमं एतत् खण्डं पश्यामःसन्दर्भगणना विना स्मार्ट सूचकाः

  1. auto_ptr(C++98, अधुना अप्रचलितम्)
  2. scoped_ptr(पुस्तकालयं वर्धयतु) .
  3. unique_ptr(C++11, अनुशंसितम्)

उपयोगं कुर्वन् शीर्षकसञ्चिकां समावेशयन्तु:#include <memory>

स्वतः_पत्र

प्रथमं एतत् कोडं विचारयामः

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

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

शल्यक्रिया दुर्घटितवती किमर्थम् ?auto_ptrस्रोतसङ्केतः

अत्र चित्रविवरणं सम्मिलितं कुर्वन्तु

भवन्तः द्रष्टुं शक्नुवन्ति यत् प्रतिलिपिनिर्माणं आगच्छन्तं वस्तु आह्वयिष्यतिreleaseविधिः, एषः विधिः स्थापयितुं_Rightसूचितं संसाधनं नूतनं प्रति प्रत्यागच्छतिauto_ptrविषयः, तत्सहकालं च_Right(वृद्धःauto_ptr)इत्यस्य_Myptrइति सेट् कृतम्nullptr

संक्षेपेण पुरातनं स्थापयितुं भवतिauto_ptrयत् संसाधनं सूचितं तत् नूतनाय दीयतेauto_ptrधारितः, पुरातनः इति सेट् भवतिnullptr .अतः उपरिष्टात् कोडः उपयुज्यते*ptr1केवलं गलत्।auto_ptrअन्तिमः स्मार्ट-सूचकः सर्वदा संसाधनस्य प्रबन्धनं करोतु)

अतः,auto_ptrकिं तस्य उपयोगः पात्रे कर्तुं शक्यते ?

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

अतः C++ इत्यत्र अप्रचलितम् अस्तिauto_ptr , यावत् अनुप्रयोगपरिदृश्यं अतीव सरलं न भवति।तथाauto_ptrC++11 मध्ये अप्रचलितं C++17 मध्ये पूर्णतया निष्कासितम्

सारांशः - १.auto_ptrस्मार्ट-सूचकानाम् सन्दर्भगणना नास्ति, अतः ते पूर्वस्य प्रत्यक्षतया प्रतिलिपिं कृत्वा अतल्लीनप्रतिलिपिस्य समस्यां निबध्नन्तिauto_ptrइति सेट् भवन्तिnullptr, अन्तिमः एव भवतुauto_ptrसंसाधनं धारयन्तु

व्याप्ति_ptr

भवद्भिः Boost पुस्तकालयं संस्थापनीयम्, यस्मिन् शीर्षकसञ्चिकाः समाविष्टाः सन्ति ।#include <boost/scoped_ptr.hpp>उपयोगाय सज्जाः

एकवारं पश्यन्तुscoped_ptrस्रोतसङ्केतः : १.

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

द्रष्टुं शक्यतेscoped_ptrप्रतिलिपिनिर्माता, असाइनमेण्ट्-कार्यं च निजीकरणं भवति, येन वस्तु एतयोः क्रियायोः समर्थनं न करोति, आह्वानं च कर्तुं न शक्यते, यत् मौलिकरूपेण अतल्लीनप्रतिकृतीनां घटनां निवारयति

अतःscoped_ptrपात्रेषु तस्य उपयोगः कर्तुं न शक्यते यदि पात्राणि परस्परं मूल्यानि प्रतिलिपयन्ति वा नियुक्तयन्ति वा तर्हि तत् कारणं भविष्यतिscoped_ptrऑब्जेक्ट् कॉपी कन्स्ट्रक्टर् तथा असाइनमेण्ट् फंक्शन्, संकलनदोषः

scoped_ptrतथाauto_ptrभेदः: स्वामित्वेन व्याख्यातुं शक्यते,auto_ptrसंसाधनानाम् स्वामित्वं इच्छानुसारं स्थानान्तरयितुं शक्यते, यदा...scoped_ptrस्वामित्वं स्थानान्तरितम् नास्ति (यतो हि प्रतिलिपिनिर्मातारः, असाइनमेण्ट् कार्याणि च अक्षमानि सन्ति)

scoped_ptrसामान्यतया न प्रयुक्तम्

अद्वितीय_ptr

प्रथमं अवलोकयन्तुunique_ptrस्रोतसङ्केतस्य भागः : १.

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

उपरितः दृष्टः, २.unique_ptrकिञ्चित्scoped_ptrतदेव क्रियते अर्थात् प्रतिलिपिनिर्माणं, असाइनमेण्ट् अतिभारितकार्यं च अक्षमं भवति, उपयोक्तृभ्यः च उपयोगः निषिद्धः भवतिunique_ptrस्मार्ट सूचक उथल प्रतिलिपिसमस्यानां घटनां निवारयितुं स्पष्टप्रतिनिर्माणं असाइनमेण्ट् च कुर्वन्तु ।

प्रतिलिपिनिर्माता नास्ति इति कारणतःunique_ptr<int> p1(new int()); unique_ptr<int> p2(p1);स्वाभाविकतया तत् दोषपूर्णम् अस्ति

किन्तुunique_ptrrvalue सन्दर्भमापदण्डैः सह प्रतिलिपिनिर्मातृन् असाइनमेण्ट् फंक्शन् च प्रदाति, अर्थात्unique_ptrस्मार्ट सूचकाः rvalue सन्दर्भाणां माध्यमेन प्रतिलिपिनिर्माणं असाइनमेण्ट् च कार्यं कर्तुं शक्नुवन्ति, अथवा जनयितुं शक्नुवन्तिunique_ptrअस्थायीवस्तूनाम् एकं स्थानं यथा स्थापनम्unique_ptrफंक्शन् इत्यस्य रिटर्न् वैल्यू इति नाम्ना नमूना कोडः निम्नलिखितरूपेण भवति ।

// 示例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

ततः प्रयोगःunique_ptrलाभः अस्ति यत् उपयोक्तारः एतादृशं किमपि उपयोक्तुं शक्नुवन्तिunique_ptr<int> p2(move(p1));कथनम्, तत् स्पष्टतया द्रष्टुं शक्यतेp1संसाधनं प्रति स्थानान्तरितम्p2p1न अधिकानि संसाधनानि धारयन्ति यदि भवन्तः उपयुञ्जतेauto_ptrस्पष्टतया न भविष्यतिmoveलिखितं चेत् अभिप्रायः स्पष्टः नास्ति यदि भवन्तः अन्तर्निहितं स्तरं न अवगच्छन्ति तर्हि भवन्तः तस्य उपयोगं अशुद्धरूपेण करिष्यन्ति ।

तस्मिन् एव काले तःunique_ptrयथा नामतः दृश्यते, अन्ते केवलं एकः स्मार्ट-सूचकः भवितुम् अर्हति यः संसाधनं निर्दिशति अतः सन्दर्भ-गणना विना स्मार्ट-सूचकानाम् उपयोगाय प्राथमिकताम् अददात् ।unique_ptr

3. सन्दर्भगणना सह स्मार्ट सूचकाः

सन्दर्भगणना सह स्मार्ट सूचकाः मुख्यतया अन्तर्भवन्तिshared_ptrतथाweak_ptr, सन्दर्भगणना सहलाभःअर्थात् बहुविधाः स्मार्ट-सूचकाः एकमेव संसाधनं प्रबन्धयितुं शक्नुवन्ति अतः सन्दर्भगणनायुक्ताः स्मार्ट-सूचकाः के सन्ति?

सन्दर्भगणना सह: प्रत्येकस्य वस्तुनः संसाधनस्य कृते सन्दर्भगणनायाः मेलनं कुर्वन्तु ।

यदा अनेकाः स्मार्ट-सूचकाः एकस्मिन् संसाधने सूचयितुं अनुमतिः भवति तदा प्रत्येकं स्मार्ट-सूचकः संसाधनस्य सन्दर्भगणनायां 1 योजयिष्यति यदा स्मार्ट-सूचकः नष्टः भवति तदा संसाधनस्य सन्दर्भगणना अपि 1 न्यूनीकरिष्यति, येन last smart pointer will यदा संसाधनस्य सन्दर्भगणना 1 तः 0 यावत् न्यूनीभवति तदा तस्य अर्थः अस्ति यत् संसाधनं मुक्तं कर्तुं शक्यते अन्तिमस्य स्मार्ट सूचकस्य विध्वंसकः संसाधनस्य विमोचनं सम्पादयति ।

  • यदा सन्दर्भगणना भवतिमाइनस १ न ०When , वर्तमान स्मार्ट सूचकः अस्य संसाधनस्य उपयोगं न करोति, परन्तु अन्ये स्मार्ट सूचकाः सन्ति यत् एतत् संसाधनं उपयुज्यन्ते वर्तमानः स्मार्ट सूचकः एतत् संसाधनं नाशयितुं न शक्नोति तथा च केवलं प्रत्यक्षतया गन्तुं शक्नोति
  • यदा सन्दर्भगणना भवति१ तः ० पर्यन्तं न्यूनीकरोतुयदा, अस्य अर्थः अस्ति यत् वर्तमानः स्मार्ट-सूचकः अस्य संसाधनस्य उपयोगाय अन्तिमः स्मार्ट-सूचकः अस्ति, अतः अस्य संसाधनस्य विमोचनस्य उत्तरदायी अस्ति ।

सन्दर्भगणना सह स्मार्ट-सूचकस्य कार्यान्वयनस्य अनुकरणं कुर्वन्तु

पूर्वं यत् कार्यान्वितम् आसीत् तत् गृह्यताम्CSmartPtrपरिवर्तनं कृत्वा कोडं प्रत्यक्षतया अपलोड् कुर्वन्तु:

// 对资源进行引用计数的类
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

एतेन एकमेव संसाधनं प्रबन्धयितुं बहुविधाः स्मार्ट-सूचकाः सक्षमाः भवन्ति ।

परन्तु इदानीं वयं ये स्मार्ट-सूचकाः कार्यान्वयामः ते थ्रेड्-सुरक्षिताः न सन्ति, बहु-थ्रेड्-परिदृश्येषु उपयोक्तुं न शक्यन्ते ।करी द्वारा कार्यान्वितःshared_ptrतथाweak_ptrसूत्र सुरक्षितम् अस्ति!

shared_ptr पार सन्दर्भ मुद्दा

  • shared_ptrशक्तिशालीस्मार्ट सूचकाः (संसाधनानाम् सन्दर्भगणनां परिवर्तयितुं शक्नुवन्ति)
  • weak_ptrसप्ताहःस्मार्ट सूचकाः (संसाधनस्य सन्दर्भगणनायां परिवर्तनं न कुर्वन्तु)

पूर्वखण्डे वयं यत् कार्यान्वितवन्तःCSmartPtrअपि च एकः प्रबलः स्मार्ट-सूचकः (संसाधनस्य सन्दर्भगणनां परिवर्तयितुं शक्नोति)

एवं अवगन्तुं शक्यते: दुर्बलः स्मार्ट सूचकः अवलोकयति सशक्त स्मार्ट सूचकाः सशक्ताः स्मार्ट सूचकाःअवलोकयति संसाधनम् (स्मृतिः) २.

अतः,प्रबल स्मार्ट सूचक पार-सन्दर्भ (वृत्तीय सन्दर्भ) समस्या किं तर्हि?आगत्य अवलोकयतु

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

संचालन परिणाम : १.

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

यथा भवन्तः पश्यन्ति, क, ख च न नष्टौ भवतः, यस्य अर्थः अस्ति यत् क्रॉस्-रेफरेंसिंग् इत्यनेन कारणं भविष्यतिnewये संसाधनाः बहिः आगच्छन्ति ते मुक्ताः कर्तुं न शक्यन्ते, यस्य परिणामेण संसाधनस्य लीकेजः भवति!

विश्लेषणं कुर्वन्तु : १.
अत्र चित्रविवरणं सम्मिलितं कुर्वन्तुअत्र चित्रविवरणं सम्मिलितं कुर्वन्तु
बहिःmainकार्यव्याप्तिः, २.paतथाpbस्थानीयवस्तुद्वयं नष्टं भवति, तथा च कस्य खस्य च सन्दर्भगणना क्रमशः २ तः १ यावत् न्यूनीकृताः भवन्ति (विमोचनस्य शर्तः अस्ति यत् कस्य खस्य च सन्दर्भगणना न्यूनीकृता भवति to 0), एवं द्वौ कारणम्newबहिः आगतानि A तथा B वस्तुनि मुक्तुं न शक्यन्ते, येन स्मृति-लीकं भवति एषा समस्या अस्तिप्रबल स्मार्ट सूचक पार-सन्दर्भ (वृत्तीय सन्दर्भ) समस्या

समाधानम् : वस्तुनि परिभाषयन्ते सति सशक्तस्मार्टसूचकानाम् उपयोगं कुर्वन्तु, वस्तुनां सन्दर्भं कुर्वन् दुर्बलस्मार्टसूचकानाम् उपयोगं कुर्वन्तु ।

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

संचालन परिणाम : १.

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

द्रष्टुं शक्यते, बहिःmainकार्यव्याप्तिः, २.paतथाpbस्थानीयवस्तुद्वयं नष्टं भवति, तथा च कवस्तु B इत्यस्य च सन्दर्भगणना क्रमशः १ तः ० पर्यन्तं न्यूनीकृताः भवन्ति, येन क, ख च मुक्तुं शर्ताः प्राप्यन्ते अतःnewबहिः आगतं क वस्तु, ख वस्तु च नष्टौ, येन समस्यायाः समाधानं जातम् ।प्रबल स्मार्ट सूचक पार-सन्दर्भ (वृत्तीय सन्दर्भ) समस्या

अत्र चित्रविवरणं सम्मिलितं कुर्वन्तु
द्रष्टुं शक्यते, २.weak_ptrदुर्बल स्मार्ट सूचकाः संसाधनस्य सन्दर्भगणनां परिवर्तनं न करिष्यन्ति, यस्य अर्थः अस्ति यत् दुर्बलः स्मार्टसूचकः केवलं वस्तु जीवितः अस्ति वा (सन्दर्भगणना 0 अस्ति वा), संसाधनस्य उपयोगं कर्तुं न शक्नोति वा इति अवलोकयति

ततः यदि अस्मिन् समये तर्हि A वर्गे योजयन्तुvoid testA() { cout << "非常好用的方法!!" << endl; }एतादृशः विधिः B इत्यस्य वृद्धिं करोतिvoid func() { _ptra->testA(); }एतत् पद्धतिं आह्वयितुं कुशलम् वा ?

  • मा शक्नोति, यतः दुर्बलः स्मार्ट-सूचकः केवलं प्रेक्षकः एव अस्ति तथा च संसाधनानाम् उपयोगं कर्तुं न शक्नोति, अर्थात् सः न प्रदाति*तथा->संचालकानाम् अतिभारितकार्यं कच्चा सूचकानाम् सदृशानि कार्याणि उपयोक्तुं न शक्नुवन्ति

समाधानं:

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

यद्यपि weak_ptrन वस्तुनः स्वामित्वं करोति, परन्तु तत् गन्तुं शक्नोतिlock()विधिः वस्तुनः सूचकं प्राप्तुं प्रयततेshared_ptr, यदि वस्तु अद्यापि जीवति (अर्थात् अन्ये सन्तिshared_ptrतत् दर्शयतु), २.lock()वस्तुं प्रति सूचकं प्रत्यागमिष्यतिshared_ptr;अन्यथा रिक्तं मूल्यं प्रत्यागमिष्यतिshared_ptr

उपयुञ्जताम्‌lock()विधिनां कृते एकः विशिष्टः परिदृश्यः तदा भवति यदा अस्थायी अभिगमनस्य आवश्यकता भवतिweak_ptrसूचितं वस्तु, तत्सहकालं च वस्तुनः आयुःगणनां वर्धयितुम् न इच्छन्ति

संचालन परिणाम : १.

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

अस्मिन् क्षणे भवन्तः द्रष्टुं शक्नुवन्ति यत् एतत् सम्यक् आहूतम् आसीत्!

4. बहुधागाः साझावस्तूनाम् अभिगमनसमये थ्रेड् सुरक्षासमस्याः

एकं पश्यामःबहुधातुभ्यः साझावस्तूनाम् अभिगमनसमये थ्रेड् सुरक्षासमस्याः: थ्रेड् A तथा thread B एकं साझावस्तुं अभिगन्तुं शक्नुवन्ति यदि thread A object इत्यस्य नाशं करोति तर्हि thread B shared object इत्यस्य सदस्यविधिं आह्वयितुं आवश्यकं भवति, thread A इत्यनेन object इत्यस्य नाशः समाप्तः स्यात्, thread B इत्येतत् न करिष्यति यदि भवान् वस्तुं प्राप्तुं प्रयतते तर्हि अप्रत्याशितदोषः भविष्यति ।

प्रथमं निम्नलिखितसङ्केतं पश्यन्तु ।

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

निष्पादनेq->testA();यदा एतत् वचनं क्रियते तदा...mainसाझीकृतं वस्तु सूत्रेण नष्टं यत् स्पष्टतया अयुक्तम् ।

यदि उत्तीर्णं कर्तुम् इच्छसिqयदि सूचकः A ऑब्जेक्ट् अभिगन्तुं इच्छति तर्हि A ऑब्जेक्ट् जीवितः अस्ति वा इति निर्धारयितुं आवश्यकं यदि A ऑब्जेक्ट् जीवितम् अस्ति तर्हि आह्वानं कुर्वन्तुtestAविधिना समस्या नास्ति यदि A वस्तु नष्टं जातं तर्हि आह्वानं कुर्वन्तुtestA समस्या अस्ति!इत्यर्थःqA object - मध्ये प्रवेशं कुर्वन् भवद्भिः ज्ञातव्यं यत् A object जीवितम् अस्ति वा इति एतस्याः समस्यायाः समाधानं कथं करणीयम् ?

एतत् कोडं पश्यामः :

// 子线程
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

संचालन परिणाम : १.

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

भवन्तः द्रष्टुं शक्नुवन्ति यत् एतत् धावतिsp->testA();,यतःmainसूत्रम् आहूतवान्t1.join()विधिः बालसूत्रस्य समाप्तिम् प्रतीक्षते अस्मिन् समयेwpउत्तीर्णःlockसफलतया पदोन्नतःsp

उपर्युक्तं कोडं परिवर्तयन्तु : १.

// 子线程
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

संचालन परिणाम : १.

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

यथा भवन्तः पश्यन्ति, वयं scope सेट् कुर्मः अपि च सेट् कुर्मःt1सूत्राणि पृथक् कर्तुं स्मार्ट सूचकाः भवतुpयदा A व्याप्तेः बहिः नष्टः भवति तदा अस्मिन् समये तस्य मुद्रणं भविष्यतिA对象已经析构,不能再访问!, अर्थात्wpउत्तीर्णःlockसफलतया उन्नयनं कर्तुं असफलम्sp

उपर्युक्तं सूत्रसुरक्षाविषयः यदा बहुसूत्राणि साझावस्तूनि अभिगच्छन्ति एतत् सन्दर्भःshared_ptrतथाweak_ptrएकः विशिष्टः अनुप्रयोगः ।

5. स्मार्ट पॉइंटर डिलीटर

6. shared_ptr इत्यस्य स्थाने make_shared इत्यस्य उपयोगः अनुशंसितः अस्ति


सन्दर्भलेखः १.C++ स्मार्ट सूचकानाम् गहनबोधः