Akıllı işaretçi nedir ve ne zaman kullanmalıyım?
Akıllı işaretçi nedir ve ne zaman kullanmalıyım?
Yanıtlar:
GÜNCELLEME
Bu cevap oldukça eskidir ve o zaman Boost kütüphanesi tarafından sağlanan akıllı işaretçiler olan 'iyi' olanı açıklar. C ++ 11 yılından bu yana, standart kütüphane yeterli akıllı işaretçileri türlerini sağlamıştır ve böylece kullanma alışkanlığı olmalıdır std::unique_ptr
, std::shared_ptr
ve std::weak_ptr
.
Ayrıca vardı std::auto_ptr
. Kapsamlı bir işaretçi gibiydi, ancak kopyalanması için "özel" tehlikeli yeteneğe de sahipti - bu da beklenmedik bir şekilde sahipliği devretti.
C ++ 11'de kullanımdan kaldırıldı ve C ++ 17'de kaldırıldı , bu yüzden kullanmamalısınız.
std::auto_ptr<MyObject> p1 (new MyObject());
std::auto_ptr<MyObject> p2 = p1; // Copy and transfer ownership.
// p1 gets set to empty!
p2->DoSomething(); // Works.
p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.
ESKİ CEVAP
Akıllı işaretçi, işaretlenen nesnenin ömrünü yönetmek için 'ham' (veya 'çıplak') C ++ işaretçisini saran bir sınıftır. Tek bir akıllı işaretçi türü yoktur, ancak hepsi pratik bir şekilde ham bir işaretçi soyutlamaya çalışır.
Akıllı işaretçiler ham işaretçiler yerine tercih edilmelidir. İşaretçiler kullanmanız gerektiğini düşünüyorsanız (önce gerçekten yaparsanız düşünün ), normalde akıllı bir işaretçi kullanmak istersiniz, çünkü bu ham işaretçilerle ilgili sorunların çoğunu hafifletebilir, özellikle nesneyi silmeyi ve bellek sızıntısını unutur.
Ham işaretçilerle, programcı artık yararlı olmadığında nesneyi açıkça yok etmek zorundadır.
// Need to create the object to achieve some goal
MyObject* ptr = new MyObject();
ptr->DoSomething(); // Use the object in some way
delete ptr; // Destroy the object. Done with it.
// Wait, what if DoSomething() raises an exception...?
Karşılaştırma ile akıllı bir işaretçi, nesnenin ne zaman imha edildiğine ilişkin bir politika tanımlar. Hâlâ nesneyi yaratmanız gerekiyor, ancak artık nesneyi yok etme konusunda endişelenmenize gerek yok.
SomeSmartPtr<MyObject> ptr(new MyObject());
ptr->DoSomething(); // Use the object in some way.
// Destruction of the object happens, depending
// on the policy the smart pointer class uses.
// Destruction would happen even if DoSomething()
// raises an exception
Kullanımdaki en basit politika, boost::scoped_ptr
veya tarafından uygulanan gibi akıllı işaretçi sarma nesnesinin kapsamını içerir std::unique_ptr
.
void f()
{
{
std::unique_ptr<MyObject> ptr(new MyObject());
ptr->DoSomethingUseful();
} // ptr goes out of scope --
// the MyObject is automatically destroyed.
// ptr->Oops(); // Compile error: "ptr" not defined
// since it is no longer in scope.
}
O Not std::unique_ptr
örnekleri kopyalanamaz. Bu, işaretçinin birden çok kez silinmesini (yanlış) önler. Bununla birlikte, aradığınız referansları aradığınız diğer işlevlere iletebilirsiniz.
std::unique_ptr
nesnenin ömrünü belirli bir kod bloğuna bağlamak istediğinizde veya başka bir nesneye üye verileri olarak gömdüğünüzde, diğer nesnenin ömründe yararlıdır. Nesne, içeren kod bloğundan çıkılıncaya veya içeren nesne yok oluncaya kadar var olur.
Daha karmaşık bir akıllı işaretçi ilkesi, işaretçiyi saymak için başvuru içerir. Bu, işaretçinin kopyalanmasına izin verir. Nesneye son "başvuru" yok edildiğinde, nesne silinir. Bu politika boost::shared_ptr
ve tarafından uygulanır std::shared_ptr
.
void f()
{
typedef std::shared_ptr<MyObject> MyObjectPtr; // nice short alias
MyObjectPtr p1; // Empty
{
MyObjectPtr p2(new MyObject());
// There is now one "reference" to the created object
p1 = p2; // Copy the pointer.
// There are now two references to the object.
} // p2 is destroyed, leaving one reference to the object.
} // p1 is destroyed, leaving a reference count of zero.
// The object is deleted.
Referans sayılan işaretçiler, nesnenizin ömrü çok daha karmaşık olduğunda ve kodun belirli bir bölümüne veya başka bir nesneye doğrudan bağlı olmadığında çok kullanışlıdır.
Sayılan işaretçilerin referansı için bir dezavantaj vardır - sarkan bir referans oluşturma olasılığı:
// Create the smart pointer on the heap
MyObjectPtr* pp = new MyObjectPtr(new MyObject())
// Hmm, we forgot to destroy the smart pointer,
// because of that, the object is never destroyed!
Başka bir olasılık dairesel referanslar oluşturmaktır:
struct Owner {
std::shared_ptr<Owner> other;
};
std::shared_ptr<Owner> p1 (new Owner());
std::shared_ptr<Owner> p2 (new Owner());
p1->other = p2; // p1 references p2
p2->other = p1; // p2 references p1
// Oops, the reference count of of p1 and p2 never goes to zero!
// The objects are never destroyed!
Bu soruna geçici bir çözüm bulmak için <a0> </a0>, Boost ve C ++ 11 weak_ptr
zayıf bir (sayılmamış) bir başvuru tanımlamak için bir tanımladı shared_ptr
.
std::auto_ptr<MyObject> p1 (new MyObject());
yerine mi demek istiyorsun std::auto_ptr<MyObject> p1 (new Owner());
?
const std::auto_ptr
C ++ 03 ile sıkışıp kalırsanız, A'nın kullanımı güvenlidir. C ++ 11'e erişene kadar pimpl deseni için çok kullandım.
İşte modern C ++ (C ++ 11 ve üstü) günleri için basit bir cevap:
std::unique_ptr
Aynı nesneye birden başvuruları tutma niyetinde olmadığı zaman. Örneğin, bir kapsam girerken ayrılan ve kapsamdan çıkıldığında ayrılan belleğe bir işaretçi için kullanın.std::shared_ptr
yapmanız birden fazla yerden sizin nesneye başvurmak istediğinizde - ve tüm bu referanslar gitmiş kendilerini olana kadar nesne de-tahsis olmak istemiyorum.std::weak_ptr
bunun (onlar sadece nesne dereference çalıştığınızda gitmiş dikkat edeceğiz böylece) görmezden ve geri_ver için ok kendisi için referanslara - yapmanız birden fazla yerden sizin nesneye başvurmak istediğinizde.boost::
Akıllı işaretçileri kullanmanız veya std::auto_ptr
gerekmesi durumunda okuyabileceğiniz özel durumlar dışında kullanmayın .T*
etmektir std::unique_ptr<T>
nedir std::weak_ptr<T>
etmektirstd::shared_ptr<T>
Akıllı işaretçi , otomatik bellek dağıtımı, referans sayımı vb. Gibi bazı ek işlevlere sahip işaretçi benzeri bir türdür.
Küçük giriş sayfasında bulunabilir Akıllı İşaretçiler - Ne, Neden, Hangi? .
Basit akıllı işaretçi türlerinden biri std::auto_ptr
(C ++ standardının 20.4.5 bölümü), hafızanın kapsam dışındayken otomatik olarak yeniden yerleştirilmesine izin veren ve daha az esnek olmasına rağmen, istisnalar atıldığında basit işaretçi kullanımından daha sağlam olanıdır.
Başka bir uygun tip, boost::shared_ptr
referans sayımını uygulayan ve nesneye hiçbir referans kalmadığında otomatik olarak belleği yeniden yerleştirir. Bu, bellek sızıntılarını önlemeye yardımcı olur ve uygulanması kolaydır RAII .
Konu, David Vandevoorde, Nicolai M. Josuttis tarafından yazılan "C ++ Templates: The Complete Guide" kitabında ayrıntılı olarak ele alınmıştır. , bölüm Bölüm 20. Akıllı İşaretçiler . Kapsanan bazı konular:
std::auto_ptr
Sahipliği yanlışlıkla aktarabileceğiniz için uyarı onaylanmadı ve son derece cesaret kırıcı. - C ++ 11 Boost, kullanım ihtiyacı ortadan kaldırır: std::unique_ptr
, std::shared_ptr
vestd::weak_ptr
Chris, Sergdev ve Llyod tarafından sağlanan tanımlar doğrudur. Daha basit bir tanımlamayı tercih ediyorum, sadece hayatımı basit tutmak için: Akıllı bir işaretçi ->
ve *
operatörleri aşırı yükleyen bir sınıftır . Senin nesne semantik bir işaretçi gibi görünüyor ama bunu referans sayma, otomatik imha vb dahil yolu soğutucu şeyler yapmak yapabileceği hangi araçlar
shared_ptr
ve auto_ptr
çoğu durumda yeterlidir, ama küçük huyların kendi seti ile birlikte geliyor.
Akıllı işaretçi, "char *" gibi normal bir işaretçi gibi, işaretçinin kendisi kapsam dışına çıktığında işaret ettiği şey de silinir. "->" kullanarak normal bir işaretçi gibi kullanabilirsiniz, ancak veriler için gerçek bir işaretçiye ihtiyacınız varsa kullanamazsınız. Bunun için "& * ptr" kullanabilirsiniz.
Aşağıdakiler için yararlıdır:
Yeni ile tahsis edilmesi gereken, ancak bu yığındaki bir şeyle aynı ömre sahip olmak istediğiniz nesneler. Nesne akıllı bir işaretçiye atanırsa, program bu işlevden / bloktan çıktığında silinir.
Sınıfların veri üyeleri, böylece nesne silindiğinde, sahip olunan tüm veriler de, yıkıcıda herhangi bir özel kod olmadan silinir (yıkıcının sanal olduğundan emin olmanız gerekir, bu da her zaman yapılacak iyi bir şeydir) .
Sen olabilir değil zaman akıllı işaretçi kullanmak istiyorum:
Ayrıca bakınız:
Çoğu akıllı işaretçi sizin için işaretçiyi-nesnenin atılmasını sağlar. Çok kullanışlıdır, çünkü artık nesneleri manuel olarak elden çıkarmayı düşünmek zorunda değilsiniz.
En yaygın kullanılan akıllı işaretçiler std::tr1::shared_ptr
(veya boost::shared_ptr
) ve daha az yaygın olanlardır std::auto_ptr
. Düzenli kullanımını tavsiye ederimshared_ptr
.
shared_ptr
çok yönlüdür ve nesnelerin "DLL sınırlarının ötesine geçmesi" gereken durumlar ( libc
kodunuz ve DLL'ler arasında farklı s kullanılması durumunda yaygın kabus durumu) dahil olmak üzere çok çeşitli bertaraf senaryolarıyla ilgilenir .
Akıllı işaretçi, işaretçi gibi davranan, ancak ek olarak inşaat, imha, kopyalama, taşıma ve kayıttan çıkarma üzerinde denetim sağlayan bir nesnedir.
Biri kendi akıllı işaretçisini uygulayabilir, ancak birçok kütüphane aynı zamanda her biri farklı avantaj ve dezavantajlara sahip akıllı işaretçi uygulamaları sağlar.
Örneğin, Boost aşağıdaki akıllı işaretçi uygulamalarını sağlar:
shared_ptr<T>
T
nesnenin artık ne zaman gerekli olmadığını belirlemek için bir referans sayısı kullanma işaretçisidir .scoped_ptr<T>
kapsam dışına çıktığında otomatik olarak silinen bir işaretçi. Atama yapılamaz.intrusive_ptr<T>
başka bir referans sayma işaretçisidir. Daha iyi performans sağlar shared_ptr
, ancak türün T
kendi referans sayma mekanizmasını sağlamasını gerektirir .weak_ptr<T>
shared_ptr
dairesel referanslardan kaçınmak için birlikte çalışan zayıf bir göstericidir .shared_array<T>
gibidir shared_ptr
, ancak dizileri için T
.scoped_array<T>
gibidir scoped_ptr
, ancak dizileri için T
.Bunlar her birinin sadece bir doğrusal açıklamasıdır ve ihtiyaca göre kullanılabilir, daha fazla ayrıntı ve örnek için Boost'un belgelerine bakabilirsiniz.
Ayrıca, C ++ standart kitaplığı üç akıllı işaretçi sağlar; std::unique_ptr
benzersiz sahiplik, std::shared_ptr
paylaşılan sahiplik ve std::weak_ptr
. std::auto_ptr
C ++ 03'te mevcuttu ancak artık kullanımdan kaldırıldı.
scoped_ptr
yerel olarak beyan edilmediğini açıklayın const unique_ptr
- kapsamdan çıkıldığında da silinir.
İşte benzer cevaplar için Bağlantı: bağlantısı http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html
Akıllı işaretçi, normal bir işaretçi gibi davranan, görünen ve hisseden ancak daha fazla işlevsellik sunan bir nesnedir. C ++ 'da, akıllı işaretçiler bir işaretçiyi kapsülleyen ve standart işaretçi işleçlerini geçersiz kılan şablon sınıfları olarak uygulanır. Düzenli göstergelere göre bir takım avantajları vardır. Null işaretçileri veya bir yığın nesnesine işaretçiler olarak başlatılmaları garanti edilir. Boş gösterici üzerinden aktarım kontrol edilir. Hiçbir zaman silmek gerekmez. Nesneler, son işaretçisi gittiğinde otomatik olarak serbest bırakılır. Bu akıllı işaretçilerle ilgili önemli bir sorun, normal işaretçilerin aksine kalıtıma saygı göstermemesidir. Akıllı işaretçiler polimorfik kod için çekici değildir. Aşağıda akıllı işaretçilerin uygulanması için bir örnek verilmiştir.
Misal:
template <class X>
class smart_pointer
{
public:
smart_pointer(); // makes a null pointer
smart_pointer(const X& x) // makes pointer to copy of x
X& operator *( );
const X& operator*( ) const;
X* operator->() const;
smart_pointer(const smart_pointer <X> &);
const smart_pointer <X> & operator =(const smart_pointer<X>&);
~smart_pointer();
private:
//...
};
Bu sınıf, X tipi bir nesneye akıllı bir işaretçi uygular. Nesnenin kendisi yığın üzerinde bulunur. İşte nasıl kullanılır:
smart_pointer <employee> p= employee("Harris",1333);
Aşırı yüklenmiş diğer operatörler gibi p de normal bir işaretçi gibi davranacaktır,
cout<<*p;
p->raise_salary(0.5);
http://en.wikipedia.org/wiki/Smart_pointer
Bilgisayar biliminde akıllı işaretçi, otomatik çöp toplama veya sınır denetimi gibi ek özellikler sağlarken işaretçiyi simüle eden soyut bir veri türüdür. Bu ek özellikler, verimliliği korurken işaretçilerin yanlış kullanımından kaynaklanan hataları azaltmayı amaçlamaktadır. Akıllı işaretçiler genellikle bellek yönetimi amacıyla kendilerini işaret eden nesneleri izler. İşaretçilerin kötüye kullanılması önemli bir hata kaynağıdır: işaretçiler kullanılarak yazılmış bir program tarafından yapılması gereken sabit ayırma, yeniden konumlandırma ve referans, bazı bellek sızıntılarının oluşmasını büyük olasılıkla mümkün kılar. Akıllı işaretçiler, kaynak ayırmayı otomatik hale getirerek bellek sızıntılarını önlemeye çalışır: bir nesneye işaretçi (veya bir dizi işaretçideki son) yok edildiğinde,
T bu öğreticide bir sınıf olsun C ++ işaretçiler 3 tipe ayrılabilir:
1) Ham işaretçiler :
T a;
T * _ptr = &a;
Hafıza adresini hafızadaki bir yere tutarlar. Programları takip etmek zorlaştıkça dikkatli kullanın.
Sabit veri veya adrese sahip işaretçiler {Geriye doğru okuyun}
T a ;
const T * ptr1 = &a ;
T const * ptr1 = &a ;
Sabit bir veri türü T olan işaretçi. Yani işaretçiyi kullanarak veri türünü değiştiremezsiniz. yani *ptr1 = 19
; çalışmayacak. Ancak işaretçiyi hareket ettirebilirsiniz. yani ptr1++ , ptr1--
; vb çalışacaktır. Geriye doğru okuyun: const olan T tipine işaretçi
T * const ptr2 ;
T veri tipine ilişkin sabit bir işaretçi. Yani işaretçiyi hareket ettiremezsiniz ancak işaretçinin işaret ettiği değeri değiştirebilirsiniz. yani *ptr2 = 19
işe yarayacak ama ptr2++ ; ptr2--
vb işe yaramayacak. Geriye doğru okuyun: T türüne sabit gösterici
const T * const ptr3 ;
Sabit veri türü T için bir sabit işaretçi. Yani işaretçiyi hareket ettiremezsiniz ya da veri türü işaretçisini işaretçi olarak değiştiremezsiniz. yani. ptr3-- ; ptr3++ ; *ptr3 = 19;
çalışmayacak
3) Akıllı İşaretçiler : { #include <memory>
}
Paylaşılan İşaretçi :
T a ;
//shared_ptr<T> shptr(new T) ; not recommended but works
shared_ptr<T> shptr = make_shared<T>(); // faster + exception safe
std::cout << shptr.use_count() ; // 1 // gives the number of "
things " pointing to it.
T * temp = shptr.get(); // gives a pointer to object
// shared_pointer used like a regular pointer to call member functions
shptr->memFn();
(*shptr).memFn();
//
shptr.reset() ; // frees the object pointed to be the ptr
shptr = nullptr ; // frees the object
shptr = make_shared<T>() ; // frees the original object and points to new object
İşaretçinin işaret ettiği nesneye kaç "şeyin" işaret ettiğini izlemek için referans sayımı kullanılarak uygulanır. Bu sayı 0 olduğunda, nesne otomatik olarak silinir, yani nesneyi işaret eden tüm share_ptr kapsam dışında kaldığında nesne silinir. Bu, yeni kullanarak ayırdığınız nesneleri silmek zorunda kalmanın baş ağrısından kurtulur.
Zayıf İşaretçi: Paylaşılan İşaretçi kullanılırken ortaya çıkan döngüsel referansla başa çıkmanıza yardımcı olur İki paylaşılan işaretçi tarafından işaretlenmiş iki nesneniz varsa ve birbirlerinin paylaşılan işaretçisine işaret eden dahili bir paylaşılan işaretçi varsa, döngüsel bir referans olur ve nesne paylaşılan işaretçiler kapsam dışına çıktığında silinir. Bu sorunu çözmek için, iç üyeyi paylaşılan_ptr değerinden zayıf_ptr değerine değiştirin. Not: Zayıf bir işaretçinin işaret ettiği öğeye erişmek için lock () kullanın, bu bir zayıf_ptr döndürür.
T a ;
shared_ptr<T> shr = make_shared<T>() ;
weak_ptr<T> wk = shr ; // initialize a weak_ptr from a shared_ptr
wk.lock()->memFn() ; // use lock to get a shared_ptr
// ^^^ Can lead to exception if the shared ptr has gone out of scope
if(!wk.expired()) wk.lock()->memFn() ;
// Check if shared ptr has gone out of scope before access
Bakınız: std :: poor_ptr ne zaman yararlıdır?
Benzersiz İşaretçi: Özel mülkiyet ile hafif akıllı işaretçi. İşaretçi nesneleri işaretçiler arasında paylaşmadan benzersiz nesnelere işaret ettiğinde kullanın.
unique_ptr<T> uptr(new T);
uptr->memFn();
//T * ptr = uptr.release(); // uptr becomes null and object is pointed to by ptr
uptr.reset() ; // deletes the object pointed to by uptr
Benzersiz ptr ile gösterilen nesneyi değiştirmek için taşıma semantiğini kullanın
unique_ptr<T> uptr1(new T);
unique_ptr<T> uptr2(new T);
uptr2 = std::move(uptr1);
// object pointed by uptr2 is deleted and
// object pointed by uptr1 is pointed to by uptr2
// uptr1 becomes null
Referanslar: Temelde sabit işaretçiler olarak düşünülebilir, yani sabit olan ve daha iyi sözdizimi ile hareket ettirilemeyen bir işaretçi.
Bkz: C ++ 'da işaretçi değişkeni ile başvuru değişkeni arasındaki farklar nelerdir?
r-value reference : reference to a temporary object
l-value reference : reference to an object whose address can be obtained
const reference : reference to a data type which is const and cannot be modified
Referans: https://www.youtube.com/channel/UCEOGtxYTB6vo6MQ-WQ9W_nQ Bu soruyu işaret ettiği için Andre'ye teşekkür ederiz.
Akıllı işaretçi bir sınıftır, normal bir işaretçinin sarıcısıdır. Normal işaretçilerin aksine, akıllı noktanın yaşam döngüsü bir referans sayısına dayanır (akıllı işaretçi nesnesinin kaç kez atandığı). Dolayısıyla, bir akıllı işaretçi diğerine atandığında, dahili referans sayısı artı artı. Ve nesne kapsam dışına çıktığında, referans sayısı eksi eksi.
Otomatik işaretçi, benzer görünse de, akıllı işaretçiden tamamen farklıdır. Bir otomatik işaretçi nesnesi değişken kapsam dışına çıktığında kaynağı yeniden dağıtan uygun bir sınıftır. Bir dereceye kadar, bir işaretçi (dinamik olarak ayrılan belleğe) bir yığın değişkenine benzer şekilde çalışır (derleme zamanında statik olarak ayrılır).
Akıllı İşaretçiler, Bellek Ayırma, Kaynak Paylaşımı ve Aktarma konusunda endişelenmenize gerek olmayanlardır.
Bu işaretçiyi Java'da herhangi bir ayırma işlemine benzer şekilde çok iyi kullanabilirsiniz. Java Çöp Toplayıcı hile yapar, Akıllı İşaretçiler ise hile Destructors tarafından yapılır.
Mevcut cevaplar iyidir, ancak akıllı bir işaretçi çözmeye çalıştığınız sorunun (tam) cevabı olmadığında ne yapacağınızı kapsamaz.
Diğer şeylerin yanı sıra (diğer cevaplarda da iyi açıklanmıştır) akıllı bir işaretçi kullanmak, soyut bir sınıfı işlev dönüş tipi olarak nasıl kullanabiliriz? bu sorunun bir kopyası olarak işaretlenmiştir. Ancak, bir soyut (veya aslında herhangi bir) temel sınıfı C ++ 'da bir dönüş türü olarak belirtmek isteyip istemediğinizi soran ilk soru "gerçekten ne demek istiyorsun?" C ++ 'da deyimsel nesne yönelimli programlama (ve bunun diğer dillerden nasıl farklı olduğu) hakkında boost pointer konteyner kütüphanesinin belgelerinde iyi bir tartışma (daha fazla referansla) vardır.. Özetle, C ++ ile sahiplik hakkında düşünmeniz gerekir. Hangi akıllı işaretçiler size yardımcı olur, ancak tek çözüm değildir veya her zaman tam bir çözüm değildir (size polimorfik kopya vermezler) ve her zaman arayüzünüzde ortaya çıkarmak istediğiniz bir çözüm değildir (ve bir işlev geri dönüşü korkunç bir ses çıkarır) bir arayüz gibi). Örneğin, bir referans döndürmek yeterli olabilir. Ancak tüm bu durumlarda (akıllı işaretçi, işaretçi kabı veya basitçe bir referans döndürme), dönüşü bir değerden bir referans biçimine değiştirdiniz. Eğer gerçekten kopyaya ihtiyacınız varsa, daha fazla kaynak plakası "deyim" eklemeniz veya Adobe veya ++ gibi kitaplıkları kullanarak C ++ 'daki deyimsel (veya başka türlü) OOP'un ötesine geçmeniz gerekebilir Boost plaka OOP'un ötesine geçmeniz gerekebilir..