ما هو استخدام Shared_ptr(shared_ptr<Y> const & r, T * p) للتعزيز؟
-
05-07-2019 - |
سؤال
boost::shared_ptr
لديه منشئ غير عادي
template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p);
وأنا في حيرة قليلاً بشأن ما سيكون مفيدًا له.في الأساس تشترك في الملكية مع r
, ، لكن .get()
سيعود p
. لا r.get()
!
هذا يعني أنه يمكنك القيام بشيء مثل هذا:
int main() {
boost::shared_ptr<int> x(new int);
boost::shared_ptr<int> y(x, new int);
std::cout << x.get() << std::endl;
std::cout << y.get() << std::endl;
std::cout << x.use_count() << std::endl;
std::cout << y.use_count() << std::endl;
}
وسوف تحصل على هذا:
0x8c66008
0x8c66030
2
2
لاحظ أن المؤشرات منفصلة، لكن كلاهما يدعي أنهما يحتويان على use_count
2 (نظرًا لأنهم يشتركون في ملكية نفس الكائن).
لذلك int
مملوكة من قبل x
سوف تكون موجودة طالما x
أو y
موجود.وإذا فهمت المستندات بشكل صحيح، والثاني int
لا يتم تدميرها أبدًا.لقد تأكدت من ذلك من خلال برنامج الاختبار التالي:
struct T {
T() { std::cout << "T()" << std::endl; }
~T() { std::cout << "~T()" << std::endl; }
};
int main() {
boost::shared_ptr<T> x(new T);
boost::shared_ptr<T> y(x, new T);
std::cout << x.get() << std::endl;
std::cout << y.get() << std::endl;
std::cout << x.use_count() << std::endl;
std::cout << y.use_count() << std::endl;
}
هذه المخرجات (كما هو متوقع):
T()
T()
0x96c2008
0x96c2030
2
2
~T()
لذا...ما فائدة هذا البناء غير العادي الذي يشترك في ملكية مؤشر واحد، ولكن الأفعال كمؤشر آخر (لا يملكه) عند استخدامه.
المحلول
يكون ذلك مفيدًا عندما تريد مشاركة عضو في الفصل ويكون مثيل الفصل مشتركًا بالفعل، كما يلي:
struct A
{
int *B; // managed inside A
};
shared_ptr<A> a( new A );
shared_ptr<int> b( a, a->B );
يتشاركون في عدد الاستخدام والأشياء.إنه الأمثل لاستخدام الذاكرة.
نصائح أخرى
للتوسع في ليز و بيوتر الإجابات، هذا الوصف ل shared_ptr<>
"الاسم المستعار" مأخوذ من ورقة WG21، "تحسين shared_ptr
لـ C++0x، المراجعة 2":
ثالثا.دعم التعرج
Advanced users often require the ability to create a
shared_ptr
مثالp
that shares ownership with another (master)shared_ptr
q
but points to an object that is not a base of*q
.*p
may be a member or an element of*q
, ، على سبيل المثال.An interesting side effect of this increase of expressive power is that now the
*_pointer_cast
الmake_shared
factory function presented later in this document can also be implemented using only the public interface ofshared_ptr
via the aliasing constructor.تأثير:
تعمل هذه الميزة على توسيع واجهة
shared_ptr
in a backward-compatible way that increases its expressive power and is therefore strongly recommended to be added to the C++0x standard. It introduces no source- and binary compatibility issues.النص المقترح:
اضف إليه
shared_ptr
[util.smartptr.shared] the following constructor:template<class Y> shared_ptr( shared_ptr<Y> const & r, T * p );
Add the following to [util.smartptr.shared.const]:
template<class Y> shared_ptr( shared_ptr<Y> const & r, T * p );
تأثيرات: يبني أ
shared_ptr
المثال الذي يخزنp
و ملكية الاسهم معr
.الشروط اللاحقة:
get() == p && use_count() == r.use_count()
.رميات: لا شئ.
[ملحوظة: To avoid the possibility of a dangling pointer, the user of this constructor must ensure that
p
remains valid at least until the ownership group ofr
دمرت. --نهاية الملاحظة.][ملحوظة: يسمح هذا المنشئ بإنشاء ملف فارغ
shared_ptr
مثيل بمؤشر مخزن غير NULL. --نهاية الملاحظة.]
يمكنك أيضًا استخدام هذا للاحتفاظ بالمؤشرات الديناميكية، على سبيل المثال:
class A {};
class B: public A {};
shared_ptr<A> a(new B);
shared_ptr<B> b(a, dynamic_cast<B*>(a.get()));
قد يكون لديك مؤشر لبعض برامج التشغيل أو بنية بيانات واجهة برمجة التطبيقات ذات المستوى الأدنى والتي قد تخصص بيانات إضافية من خلال واجهة برمجة التطبيقات ذات المستوى الأدنى أو وسائل أخرى.في هذه الحالة قد يكون من المثير للاهتمام زيادة عدد use_count مع إرجاع البيانات الإضافية إذا كان المؤشر الأول يمتلك مؤشرات البيانات الأخرى.
لقد قمت بوضع مُنشئ الاسم المستعار لـshared_ptr قيد الاستخدام في مكتبتي الصغيرة:
http://code.google.com/p/infectorpp/ (فقط حاوية IoC البسيطة الخاصة بي)
النقطة المهمة هي أنني كنت بحاجة إلى Share_ptr من النوع المعروف ليتم إرجاعه من فئة متعددة الأشكال (لا تعرف النوع).لم أتمكن ضمنيًا من تحويل Shared_ptr إلى النوع الذي أحتاجه.
في الملف "InfectorHelpers.hpp" (السطر 72-99) يمكنك أن ترى ذلك عمليًا بالنسبة للنوع IanyShared.
يقوم مُنشئ الاسم المستعار بإنشاء Shared_ptr الذي لا يحذف المؤشرات التي تشير إليها بالفعل، ولكن ما زالوا يزيدون العداد المرجعي إلى الكائن الأصلي والتي يمكن أن تكون مفيدة للغاية.
يمكنك بشكل أساسي إنشاء مؤشر لأي شيء باستخدام مُنشئ الاسم المستعار والتهديد به كعداد مرجعي.
//my class
std::shared_ptr<T> ist;
int a; //dummy variable. I need its adress
virtual std::shared_ptr<int> getReferenceCounter(){
return std::shared_ptr<int>(ist,&a); //not intended for dereferencing
}
virtual void* getPtr(); //return raw pointer to T
الآن لدينا "عداد مرجعي" ومؤشر لمسافة T، وبيانات كافية لإنشاء شيء ما باستخدام مُنشئ الاسم المستعار
std::shared_ptr<T> aPtr( any->getReferenceCounter(), //share same ref counter
static_cast<T*>(any->getPtr()) ); //potentially unsafe cast!
أنا لا أدعي أنني اخترعت هذا الاستخدام لمنشئ الأسماء المستعارة، لكنني لم أر شخصًا آخر يفعل الشيء نفسه.إذا كنت تتساءل عما إذا كان هذا الرمز القذر يعمل، فالإجابة هي نعم.
ل "shared_ptr<B> b(a, dynamic_cast<B*>(a.get()));
"
أعتقد أنها ليست الطريقة الموصى بها باستخدام المؤشر الذكي.
يجب أن تكون الطريقة الموصى بها لإجراء هذا النوع من التحويل هي:
shared_ptr<B> b(a);
منذ أن ورد في مستند Boost ما يلي:
shared_ptr<T>
can be implicitly converted toshared_ptr<U>
بخاصة،shared_ptr<T>
is implicitly convertible toshared_ptr<T> const
, ، لshared_ptr<U>
where U is an accessible base of T, and toshared_ptr<void>
.
وبالإضافة إلى ذلك، لدينا أيضا Dynamic_pointer_castوالتي يمكنها إجراء التحويل مباشرة على كائن Smart Pointer وستكون هاتان الطريقتان أكثر أمانًا من طريقة صب المؤشر الخام يدويًا.