Buna silme izni var mı?


232

delete this;Delete-deyiminin, sınıfın o örneğinde yürütülecek son deyim olup olmadığına izin veriliyor mu? Elbette this-pointer tarafından temsil edilen nesnenin newyaratıldığından eminim .

Ben böyle bir şey düşünüyorum:

void SomeModule::doStuff()
{
    // in the controller, "this" object of SomeModule is the "current module"
    // now, if I want to switch over to a new Module, eg:

    controller->setWorkingModule(new OtherModule());

    // since the new "OtherModule" object will take the lead, 
    // I want to get rid of this "SomeModule" object:

    delete this;
}

Bunu yapabilir miyim?


13
Ana sorun delete this, sınıf ve o sınıfın nesnelerini oluşturmak için kullanılan ayırma yöntemi arasında sıkı bir bağlantı oluşturduysanız olurdu . Bu çok zayıf OO tasarımıdır, çünkü OOP'taki en temel şey, arayanların ne yaptığını bilmeyen veya umursamayan özerk sınıflar yapmaktır. Bu nedenle, uygun şekilde tasarlanmış bir sınıf, nasıl tahsis edildiğini bilmemeli veya önemsememelidir. Herhangi bir nedenden dolayı böyle tuhaf bir mekanizmaya ihtiyacınız varsa, daha iyi bir tasarımın gerçek sınıfın etrafında bir sarmalayıcı sınıfı kullanmak ve sarmalayıcının tahsisle baş etmesine izin vermek olduğunu düşünüyorum.
Lundin

İçeri giremiyor setWorkingModulemusunuz?
Jimmy T.

Yanıtlar:


238

C ++ FAQ Lite bunun için özel bir giriş içeriyor

Bence bu alıntı güzelce özetliyor

Dikkatli olduğunuz sürece, bir nesnenin intihar etmesi sorun olmaz (bunu silin).


15
Karşılık gelen FQA'nın da bazı yararlı yorumları var: yosefk.com/c++fqa/heap.html#fqa-16.15
Alexandre

1
Güvenlik için, orijinal nesnede özel yıkıcıyı yığın üzerinde veya bir dizi veya vektörün parçası olarak oluşturulmadığından emin olmak için kullanabilirsiniz.
Cem Kalyoncu

'Dikkatli' tanımlayın
CinCout

3
'Dikkatli' bağlantılı SSS makalesinde tanımlanmıştır. (FQA bağlantısı çoğunlukla sıralanırken - neredeyse her şey gibi - C ++ ne kadar kötü)
CharonX

88

Evet, delete this;(not ettiğiniz gibi) nesnenin dinamik olarak ayrıldığından emin olduğunuz sürece ve (elbette) nesneyi hiçbir zaman yok edildikten sonra kullanmaya kalkışmadığınız sürece sonuçları tanımlamıştır. Yıllar geçtikçe, delete this;başka bir işaretçiyi silmek yerine standardın özellikle ne hakkında söylediği hakkında birçok soru soruldu . Bunun cevabı oldukça kısa ve basit: hiçbir şey söylemiyor. Sadece deleteişlenenin bir nesneye veya bir nesne dizisine işaretçi belirten bir ifade olması gerektiğini söyler . Belleği serbest bırakmak için ne (eğer varsa) yeniden konumlandırma işlevini çağırması gerektiği gibi şeyler hakkında biraz ayrıntıya girer, ancak delete(§ [ifade.delete]) üzerindeki tüm bölüm delete this;özel olarak hiç bahsetmez . Destrucors bölümünden bahsediyordelete this tek bir yerde (§ [class.dtor] / 13):

Sanal bir yıkıcının tanımlanma noktasında (örtük bir tanım (15.8) dahil), dizi dışı deallocation işlevi, ifade için yıkıcı sınıfının sanal olmayan bir yıkıcısında görünen bu silme gibi belirlenir (bkz 8.3.5) ).

Bu, standardın delete this;geçerli olduğunu düşündüğü fikrini destekleme eğilimindedir - geçersiz olsaydı, türü anlamlı olmazdı. delete this;Bildiğim kadarıyla standart bahsettiğim tek yer burası .

Her neyse, bazıları delete thiskötü bir hack düşünür ve dinleyecek herkese bundan kaçınması gerektiğini söyler. Yaygın olarak belirtilen bir sorun, sınıftaki nesnelerin sadece dinamik olarak tahsis edilmesinin zorluğudur. Diğerleri bunu mükemmel makul bir deyim olarak görür ve her zaman kullanır. Şahsen, ortada bir yerdeyim: Nadiren kullanıyorum, ancak iş için doğru araç gibi göründüğünde bunu yapmaktan çekinmeyin.

Bu tekniği ilk kullandığınızda neredeyse tamamen kendine ait bir hayatı olan bir cisim var. James Kanze'nin belirttiği bir örnek, bir telefon şirketi için üzerinde çalıştığı bir faturalandırma / izleme sistemiydi. Telefon görüşmesi yapmaya başladığınızda, bir şey bunu not eder ve bir phone_callnesne oluşturur . Bu noktadan sonra, phone_callnesne telefon görüşmesinin ayrıntılarını işler (aradığınızda bağlantı kurar, aramaya ne zaman başladığını söylemek için veritabanına bir giriş ekler, konferans araması yaparsanız daha fazla kişi bağlar vb.) çağrıdaki son kişiler phone_calltelefonu kapatır , nesne son kayıt defterini yapar (örneğin, telefonu kapattığınızda söylemek için veritabanına bir giriş ekler, böylece çağrınızın ne kadar sürdüğünü hesaplayabilirler) ve sonra kendini yok eder. Ömrüphone_callsistemin geri kalanından açısından bu kadar, bu temelde tamamen keyfi var - nesne ilk kişi çağrıyı başlatır ve son kişiler çağrı ayrılırken zaman dayanmaktadır edemez kodunda herhangi sözcük kapsamına bağlarız veya bu siparişteki herhangi bir şey.

Bu tür kodlamanın ne kadar güvenilir olabileceğini önemseyen herkes için: Avrupa'nın bir kısmından veya Avrupa'nın herhangi bir bölümünden bir telefon görüşmesi yaparsanız, (en azından kısmen) kodla ele alınma olasılığı oldukça yüksektir tam olarak bunu yapar.


2
Teşekkürler, hafızamda bir yere koyacağım. Yapıcıları ve yıkıcıları özel olarak tanımladığınızı ve bu tür nesneleri oluşturmak için bazı statik fabrika yöntemlerini kullandığınızı varsayalım.
Alexandre

@Alexandre: Muhtemelen bunu çoğu durumda yapardınız - çalıştığı sistemin tüm detaylarına yakın hiçbir yerde bilmiyorum, bu yüzden emin olamıyorum.
Jerry Coffin

Sıklıkla belleğin nasıl tahsis edildiği sorununu çözme yolum bool selfDelete, üye değişkene atanan yapıcıya bir parametre eklemektir . Verilen bu, programcıya bir ilmik bağlamak için yeterli ipi vermek anlamına gelir, ancak bunu bellek sızıntılarına tercih edilir buluyorum.
MBraedley

1
@MBraedley: Ben de aynısını yaptım ama bana bir çamur gibi görünen şeylerden kaçınmayı tercih ediyorum.
Jerry Coffin

İlgilenebilecek herkes için ... tam olarak yapan kodla (en azından kısmen) ele alınma şansı oldukça yüksektir this. Evet, kod tam olarak işleniyor this. ;)
Galaxy

46

Sizi korkutuyorsa, mükemmel bir yasal kesmek var:

void myclass::delete_me()
{
    std::unique_ptr<myclass> bye_bye(this);
}

Bence delete thisdeyimsel C ++ olsa da, bunu sadece bir merak olarak sunuyorum.

Bu yapının gerçekten kullanışlı olduğu bir durum vardır - nesneden üye verilerine ihtiyaç duyan bir istisna attıktan sonra nesneyi silebilirsiniz. Nesne, atış gerçekleşene kadar geçerliliğini korur.

void myclass::throw_error()
{
    std::unique_ptr<myclass> bye_bye(this);
    throw std::runtime_exception(this->error_msg);
}

Not: std::auto_ptrBunun yerine kullanabileceğiniz C ++ 11'den daha eski bir derleyici kullanıyorsanız std::unique_ptr, aynı şeyi yapar.


Bu c ++ 11 kullanarak derlemek için alamıyorum, bunun için bazı özel derleyici seçenekleri var mı? Ayrıca bu işaretçinin hareket ettirilmesini gerektirmez mi?
Baykuş

@ Baykuş ne demek istediğinden emin değil, benim için çalışıyor: ideone.com/aavQUK . Bir oluşturma unique_ptrgelen başka unique_ptr ancak bir ham işaretçi, bir hamle gerektirir. C ++ 17'de bir şey değişmedikçe?
Mark Ransom

Ahh C ++ 14, bu yüzden olacak. Geliştirme kutusundaki c ++ 'mı güncellemem gerekiyor. Bu gece yakın zamanda ortaya çıkan gentoo sistemimi tekrar deneyeceğim!
Baykuş

25

C ++ 'ın tasarlanmasının nedenlerinden biri, kodu yeniden kullanmayı kolaylaştırmaktı. Genel olarak, C ++, sınıfın öbek üzerinde, bir dizide veya yığın üzerinde somutlaştırılması için çalışacak şekilde yazılmalıdır. "Bunu sil" çok kötü bir kodlama uygulamasıdır çünkü yığın üzerinde tek bir örnek tanımlanmışsa çalışır; ve genellikle çoğu geliştirici tarafından yığını temizlemek için kullanılan başka bir silme ifadesi olmasa iyi olur. Bunu yapmak, gelecekte hiçbir bakım programcısının, silme ifadesi ekleyerek yanlış algılanan bir bellek sızıntısını iyileştirmeyeceğini varsayar.

Şimdiki planınızın yığın üzerinde tek bir örnek tahsis etmek olduğunu önceden bilseniz bile, gelecekte şanslı bir geliştirici gelip yığın üzerinde bir örnek oluşturmaya karar verirse ne olur? Ya da, sınıfın belirli bölümlerini yığın üzerinde kullanmayı düşündüğü yeni bir sınıfa kesip yapıştırırsa ne olur? Kod "bunu sil" e ulaştığında kapanır ve silinir, ancak nesne kapsam dışına çıktığında, yıkıcıyı çağırır. Yıkıcı daha sonra tekrar silmeye çalışacak ve size ev sahipliği yapacak. Geçmişte böyle bir şey yapmak sadece programı değil, işletim sisteminin ve bilgisayarın yeniden başlatılmasını gerektiriyordu. Her durumda, bu şiddetle tavsiye EDİLMEZ ve neredeyse her zaman kaçınılmalıdır. Çaresiz, ciddi şekilde sıvalı olmalıydım,


7
+1. Neden aşağı indirildiğini anlayamıyorum. "C ++, sınıfın yığın üzerinde mi, bir dizide mi yoksa yığın üzerinde mi örnekleneceği işe yarayacak şekilde yazılmalıdır" çok iyi bir tavsiye.
Joh

1
Silmek istediğiniz nesneyi, nesneyi ve daha sonra kendisini silen özel bir sınıfa sarıp yığın tahsisini önlemek için bu tekniği kullanabilirsiniz: stackoverflow.com/questions/124880/… Gerçekten bir zamanın olmadığı zamanlar vardır. uygulanabilir alternatif. Ben sadece bir DLL işlevi tarafından başlatılan bir iş parçacığını kendi kendine silmek için bu tekniği kullandım, ancak DLL işlevi iş parçacığı bitmeden önce dönmelidir.
Felix Dombek

Kodunuzla sadece kopyalayıp yapıştıracak birisinin onu kötüye kullanmasına neden olacak şekilde programlayamazsınız
Jimmy T.

22

İzin verilir (sadece bundan sonra nesneyi kullanmayın), ancak pratikte böyle bir kod yazmazdım. Ben düşünüyorum delete thisdenilen tek fonksiyonları görünmelidir releaseveya Releaseve oluşacağı: void release() { ref--; if (ref<1) delete this; }.


Hangi benim her projesinde tam olarak bir kez ... :-)
cmaster - reinstate monica

15

Bileşen Nesne Modeli (COM) delete thisyapısında, Releasesulandırılmış nesneyi serbest bırakmak istediğinizde çağrılan yöntemin bir parçası olabilir :

void IMyInterface::Release()
{
    --instanceCount;
    if(instanceCount == 0)
        delete this;
}

8

Bu, referans sayılan nesneler için temel deyimdir.

Referans sayma, güçlü bir deterministik çöp toplama biçimidir - nesnelerin kendileri için 'akıllı' işaretçiler vb.'ye güvenmek yerine KENDİ yaşamlarını yönetmelerini sağlar. Temel nesneye yalnızca, işaretçilerin gerçek nesnede bir üye tamsayısını (referans sayısı) artıracak ve azaltacak şekilde tasarlanmış "Referans" akıllı işaretçilerle erişilir.

Son referans yığından ayrıldığında veya silindiğinde, referans sayısı sıfıra gider. Nesnenin varsayılan davranışı daha sonra çöp toplama "bu silmek" için bir çağrı olacaktır - yazdığım kütüphaneler, temel sınıfta korumalı bir sanal "CountIsZero" çağrısı sağlar, böylece önbellekleme gibi şeyler için bu davranışı geçersiz kılabilirsiniz.

Bunu güvenli hale getirmenin anahtarı, kullanıcıların söz konusu nesnenin CONSTRUCTOR'una erişmesine (korumalı hale getirmesine) izin vermek değil, bunun yerine FACTORY benzeri "statik Reference CreateT (...)" gibi bazı statik üyeler yapmalarını sağlamaktır. Bu şekilde her zaman sıradan "yeni" ile oluşturulduğundan ve hiç ham işaretçinin bulunmadığından emin olursunuz, böylece "bunu sil" hiç patlamaz.


Neden sadece (singleton) sınıfı bir “ayırıcı / çöp toplayıcı”, tüm tahsisatın yapıldığı bir arayüze sahip olamazsınız ve bu sınıfın tahsis edilen nesnelerin tüm referans sayımını işlemesine izin veremiyorsunuz? Nesneleri, çöp toplama görevleriyle uğraşmaya zorlamak yerine, belirlenen amaçlarıyla tamamen ilgisiz bir şey.
Lundin

1
Ayrıca, nesnenizin statik ve yığın tahsisini engellemek için yıkıcıyı korumalı hale getirebilirsiniz.
Game_Overture

7

Bunu yapabilirsiniz. Ancak, buna atayamazsınız. Bu nedenle, bunu yapmak için belirttiğiniz neden "Görünümü değiştirmek istiyorum" çok tartışmalı görünüyor. Bence daha iyi bir yöntem, görüşü tutan nesnenin bu görüşün yerini almasıdır.

Tabii ki, RAII nesneleri kullanıyorsunuz ve bu yüzden gerçekten de delete'i çağırmanıza gerek yok ... değil mi?


4

Bu eski, cevaplanmış bir soru ama @Alexandre "Neden bunu yapmak istesin ki?" Diye sordu ve bu öğleden sonra düşündüğüm örnek bir kullanım sağlayabileceğimi düşündüm.

Eski kod. Sonunda silme obj ile çıplak işaretçiler Obj * obj kullanır.

Ne yazık ki bazen nesneyi daha uzun süre canlı tutmak için sık sık olmamak gerekir.

Bir referans sayılır akıllı işaretçi yapmayı düşünüyorum. Ama ben her yerde kullanmak olsaydı, değiştirmek için kod çok olurdu ref_cnt_ptr<Obj>. Ve çıplak Obj * ve ref_cnt_ptr'i karıştırırsanız, Obj * hala hayatta olsa bile, son ref_cnt_ptr gittiğinde nesneyi dolaylı olarak silebilirsiniz.

Bu yüzden bir explicit_delete_ref_cnt_ptr oluşturmayı düşünüyorum. Yani, silme işleminin yalnızca açık bir silme rutininde yapıldığı bir referans sayma işaretçisi. Mevcut kodun nesnenin ömrünü bildiği tek bir yerde ve nesneyi daha uzun süre canlı tutan yeni kodumda kullanmak.

Referans sayısını explicit_delete_ref_cnt_ptr olarak artırmak ve azaltmak manipüle edilir.

Ancak explicit_delete_ref_cnt_ptr yıkıcıda referans sayısı sıfır olarak göründüğünde serbest DEĞİLDİR.

Yalnızca, açık bir silme benzeri işlemde referans sayısının sıfır olduğu görülüyorsa serbest kalır. Örneğin:

template<typename T> class explicit_delete_ref_cnt_ptr { 
 private: 
   T* ptr;
   int rc;
   ...
 public: 
   void delete_if_rc0() {
      if( this->ptr ) {
        this->rc--;
        if( this->rc == 0 ) {
           delete this->ptr;
        }
        this->ptr = 0;
      }
    }
 };

Tamam, böyle bir şey. Rc'ed ptr destructor'da gösterilen nesneyi otomatik olarak silmeyen bir referans sayılan işaretçi türüne sahip olmak alışılmadık bir durumdur. Ancak bu, çıplak işaretçiler ve rc'ed işaretçilerin karıştırılmasını biraz daha güvenli hale getirebilir.

Ama şu ana kadar bunu silmeye gerek yok.

Ama sonra benim başıma geldi: eğer nesne işaret ettiğinde, pointee, referansın sayıldığını bilir, örneğin sayım nesnenin içindeyse (veya başka bir tabloda), o zaman rutin delete_if_rc0 bir yöntem olabilir pointee nesnesi (akıllı) işaretçi değil.

class Pointee { 
 private: 
   int rc;
   ...
 public: 
   void delete_if_rc0() {
        this->rc--;
        if( this->rc == 0 ) {
           delete this;
        }
      }
    }
 };

Aslında, üye bir yöntem olması gerekmez, ancak ücretsiz bir işlev olabilir:

map<void*,int> keepalive_map;
template<typename T>
void delete_if_rc0(T*ptr) {
        void* tptr = (void*)ptr;
        if( keepalive_map[tptr] == 1 ) {
           delete ptr;
        }
};

(BTW, kodun oldukça doğru olmadığını biliyorum - tüm ayrıntıları eklersem daha az okunabilir hale gelir, bu yüzden böyle bırakıyorum.)


0

Sil, nesne yığın halinde olduğu sürece yasaldır. Nesnenin yalnızca yığın olması gerekir. Bunu yapmanın tek yolu, yıkıcıyı korumaktır - bu şekilde SİLİNME SADECE sınıftan çağrılabilir, bu nedenle silme işlemini sağlayacak bir yönteme ihtiyacınız olacaktır

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.