Sanal yıkıcılar kullanılmadığında?


48

Sanal yıkıcılar hakkında defalarca aradığımı, sanal yıkıcıların amaçlarından ve neden sanal yıkıcılara ihtiyaç duyduğunuzu düşündüğüme inanıyorum. Ayrıca çoğu durumda yıkıcıların sanal olması gerektiğini düşünüyorum.

Öyleyse soru şudur: c ++ neden tüm yıkıcıları varsayılan olarak sanal olarak ayarlamıyor? veya başka sorularda:

Ne zaman sanal yıkıcılar kullanmam gerekmiyor?

Bu durumda sanal yıkıcılar kullanmamalı mıyım?

Gerekmediği halde kullanmam durumunda sanal yıkıcılar kullanmanın maliyeti nedir?


6
Peki ya sınıfınızın kalıtımsal olmaması gerekiyorsa? Standart kütüphane sınıflarının çoğuna bakın, pek azının sanal işlevleri var, çünkü bunlar kalıtsal olarak tasarlanmamış.
Bazı programcı

4
Ayrıca çoğu durumda yıkıcıların sanal olması gerektiğini düşünüyorum. Hayır! Bir şey değil. Sadece mirası kötüye kullananlar (bileşimi tercih etmek yerine) öyle düşünüyor. Tüm uygulamaları bir avuç temel sınıf ve sanal fonksiyonla gördüm.
Matthieu M.

1
@underscore_d Tipik uygulamalarda, bu tür tüm örtük şeyler devirtualize edilip optimize edilmedikçe, herhangi bir polimorfik sınıf için üretilen ekstra kod olacaktır. Ortak ABI'lerde bu, her sınıf için en az bir oy gerektirir. Sınıf düzeninin de değiştirilmesi gerekiyor. Bazı ortak arabirimlerin parçaları gibi bir sınıf yayınladıktan sonra güvenilir bir şekilde geri dönemezsiniz, çünkü tekrar değiştirmek ABI uyumluluğunu kıracaktır, çünkü genel olarak arayüz sözleşmeleri olarak sapkınlaştırılmayı beklemek açıkça mümkün değildir (eğer mümkünse).
FrankHB

1
@underscore_d "Derleme zamanında" ifadesi yanlış, ancak bunun sanal bir yıkıcının önemsiz ya da belirsiz ifadelerle olamayacağı anlamına geldiğini düşünüyorum, bu yüzden ekstra kod oluşturmanın kaçınılması zor (bu tür nesnelerin imhasını tamamen ortadan kaldırmazsanız) az ya da çok çalışma zamanı performansına zarar verir.
FrankHB

2
@underscore_d "İşaretçi" kırmızı ringa balığı gibi görünüyor. Muhtemelen üyeye bir işaretçi olmalıdır ( tanımı gereği bir işaretçi değildir ). Her zamanki ABI'lerle, üyeye bir işaretçi genellikle bir makine kelimesine sığmaz (tipik işaretçiler gibi) ve bir sınıfı polimorfik olmayandan polimorfik olana değiştirmek, işaretçinin boyutunu bu sınıfın üyesine değiştirir.
FrankHB

Yanıtlar:


41

Bir sınıfa sanal bir yıkıcı eklerseniz:

  • (tümü?) geçerli C ++ uygulamalarında, bu sınıfın her nesne örneği çalışma zamanı türü için sanal gönderme tablosuna bir işaretçi ve bu sanal gönderme tablosunun kendisi de yürütülebilir görüntüye eklenmiş olarak depolamalıdır.

  • sanal gönderme tablosunun adresi, işlemler sırasında bu tür nesnelerin paylaşılan bellekte güvenli bir şekilde paylaşılmasını önleyebilecek kesin olarak geçerli değildir.

  • gömülü bir sanal işaretçinin, bilinen bazı giriş veya çıkış formatlarıyla eşleşen bellek düzenine sahip bir sınıf oluşturmada sinir bozucu Price_Tick*olması; newGiden bir pakete veri yazmak için böyle bir sınıfın yerleştirilmesi )

  • yıkıcı, kendileri - belirli şartlar altında - hemen hemen ve bu nedenle çevrimdışı olarak gönderilmek zorunda kalabilir, oysa sanal olmayan yıkıcılar arayanın önemsiz veya ilgisiz olması durumunda inline edilebilir veya uzaklaştırılabilir.

"Miras alınmak için tasarlanmadı" argümanı, yukarıda açıklandığı gibi pratik bir şekilde daha kötüsü olmasaydı, her zaman sanal bir yıkıcıya sahip olmamak için pratik bir neden olmazdı; Ancak, bunun ne zaman ücretin ne zaman ödeneceği konusunda önemli bir kriter olduğu düşünüldüğünde: Sınıfınız temel sınıf olarak kullanılmak üzere tasarlandıysa, varsayılan olarak sanal bir yıkıcıya sahip olmak . Bu her zaman gerekli değildir, ancak bir temel sınıf işaretçisi ya da referansı kullanılarak türetilmiş bir sınıf yıkıcısı çağrılırsa, heirarşideki sınıfların yanlışlıkla tanımsız davranış olmadan daha serbest bir şekilde kullanılmasını sağlar.

“çoğu durumda yıkıcılar sanal olmalı”

Öyle değil ... birçok sınıfın böyle bir ihtiyacı yok. Onları saymak için aptalca hissetmenin gereksiz olduğu yerlere dair pek çok örnek var, ancak Standart Kütüphanenize bakın ya da destek söyleyin ve sanal yıkıcıları olmayan sınıfların büyük bir çoğunluğunun olduğunu göreceksiniz. 1.53 artışında 494 üzerinden 72 sanal yıkıcı saydım.


23

Bu durumda sanal yıkıcılar kullanmamalı mıyım?

  1. Miras almak istemeyen somut bir sınıf için.
  2. Polimorfik silmeden oluşan bir baz sınıf için. Her iki istemcide de bir işaretçiyi kullanarak Base'e polimorfik olarak silememek gerekir.

BTW,

Hangi durumlarda sanal yıkıcılar kullanılmalı?

Polimorfik silinmesi olan bir baz sınıfları için.


7
# 2 için +1, özellikle polimorfik silmeden . Eğer yıkıcınız asla bir temel işaretçi ile çalıştırılamazsa, sanal hale getirmek gereksiz ve gereksizdir, özellikle de sınıfınız daha önce sanal değilse (yani RTTI ile yeni şişirilmiş hale gelir). Herb Sutter'ın tavsiye ettiği gibi, herhangi bir kullanıcının bu ihlale karşı korunmasını sağlamak için, temel sınıfın dtorunu korumalı ve sanal olmayan bir hale getirin, böylece sadece türetilmiş bir yıkıcı tarafından / sonrasında kullanılabilir.
underscore_d

@underscore_d, cevaplarda kaçırdığım önemli bir nokta, miras varlığında olduğu gibi, sanal bir kurucuya ihtiyaç duymadığım tek durum, asla ihtiyaç duyulmamasını sağlayabildiğim zamandı
eskiden

14

Gerekmediği halde kullanmam durumunda sanal yıkıcılar kullanmanın maliyeti nedir?

Sokulması maliyeti bir (kalıtsal veya sınıf tanımının bir parçası) bir olasılıkla çok dik bir (ya da nesneye bağlı değildir), böylece gibi, nesne için saklanan bir sanal işaretçi başlangıç maliyeti bir sınıfa sanal işlevi:

struct Integer
{
    virtual ~Integer() {}
    int value;
};

Bu durumda, hafıza maliyeti nispeten büyüktür. Bir sınıf örneğinin gerçek bellek boyutu şimdi 64 bit mimarilerde şöyle görünür:

struct Integer
{
    // 8 byte vptr overhead
    int value; // 4 bytes
    // typically 4 more bytes of padding for alignment of vptr
};

Bu Integersınıf için toplam 4 bayt yerine toplam 16 bayttır. Bunların bir milyonunu bir dizide saklarsak, 16 megabayt bellek kullanımı ile sonuçlanır: tipik 8 MB L3 CPU önbelleğinin iki katı büyüklüğündedir ve böyle bir diziyi tekrar tekrar yinelemek 4 megabayt eşdeğerinden daha yavaş olabilir ek önbellek özlüyor ve sayfa hataları sonucu sanal işaretçi olmadan.

Ancak bu sanal işaretçi nesne başına maliyeti, daha fazla sanal işlevle artmaz. Bir sınıfta 100 sanal üye işlevine sahip olabilirsiniz ve örnek başına ek yük hala tek bir sanal işaretçi olabilir.

Sanal işaretçi genellikle bir genel bakış açısından en acil endişe kaynağıdır. Ancak, örnek başına sanal bir işaretçiye ek olarak, sınıf başına maliyettir. Sanal işlevli her sınıf, sanal bir işlev vtableçağrısı yapıldığında adresleri gerçekte çağırması gereken işlevlere (sanal / dinamik gönderme) depolayan bir bellek oluşturur . vptrBu sınıfa özel noktaların daha sonra örnek başına saklanan vtable. Bu genel gider daha az endişe vericidir, ancak bu genel giderin karmaşık bir kod tabanındaki bin sınıfa gereksiz yere ödenmesi durumunda ikili boyutunuzu şişirebilir ve bir miktar çalışma zamanı maliyeti ekleyebilir, örneğin vtablemaliyetin bu tarafı gerçekten daha fazla ve Karışımda daha fazla sanal işlev.

Performans açısından kritik alanlarda çalışan Java geliştiricileri, bu tür bir ek yükü çok iyi anlarlar (genellikle boks bağlamında tanımlanırlar), çünkü Java kullanıcı tanımlı bir tür, merkezi bir objecttemel sınıftan dolaylı olarak miras alır ve Java'daki tüm işlevler dolaylı olarak sanaldır (geçersiz kılınabilir) ) Aksi belirtilmedikçe doğada. Sonuç olarak, bir Java, Integeraynı şekilde vptrörneklenen bu stil meta verilerinin bir sonucu olarak 64 bit platformlarda 16 bayt bellek gerektirme eğilimindedir ve Java'da, intbir çalışma zamanı ödemeden bir sınıfa tek bir şeyi sarmak genellikle imkansızdır. bunun için performans maliyeti.

Öyleyse soru şudur: c ++ neden tüm yıkıcıları varsayılan olarak sanal olarak ayarlamıyor?

C ++, "kullandıkça öde" türünde bir zihniyetin yanı sıra C'den devralınan pek çok çıplak metal donanım tabanlı tasarımla performansı gerçekten destekliyor. dahil olan her bir sınıf / örnek. Performans, C ++ gibi bir dili kullanmanızın temel nedenlerinden biri değilse, C ++ dilinin çoğu daha az güvenli olduğundan ve ideal performansta olduğundan daha zor olduğundan diğer programlama dillerinden daha fazla yararlanabilirsiniz. böyle bir tasarımı tercih etmenin kilit nedeni.

Ne zaman sanal yıkıcılar kullanmam gerekmiyor?

Oldukça sık. Eğer bir sınıf kalıtsal olarak tasarlanmamışsa, sanal bir yıkıcıya ihtiyaç duymaz ve ihtiyaç duymadığı bir şey için büyük olasılıkla büyük bir ek ödeme yapar. Aynı şekilde, bir sınıf kalıtılmak üzere tasarlanmış olsa bile, ancak bir alt işaretçi türünü asla bir temel işaretçi ile silmeseniz bile, sanal bir yıkıcı gerektirmez. Bu durumda, güvenli bir uygulama, korunmalı, sanal olmayan bir yıkıcıyı tanımlamaktır.

class BaseClass
{
protected:
    // Disallow deleting/destroying subclass objects through `BaseClass*`.
    ~BaseClass() {}
};

Bu durumda sanal yıkıcılar kullanmamalı mıyım?

Sanal yıkıcılar kullanmanız gerektiğinde ele alınması daha kolaydır . Oldukça çoğu zaman kod tabanınızdaki daha fazla sınıf kalıtım için tasarlanmayacaktır.

std::vectorO zaman bu baz işaretçi silme sorunu (eğilimli olacak şekilde, örneğin, miras olarak tasarlanmamıştır ve tipik (çok titrek tasarımı) miras olmamalıdır std::vectorbeceriksiz ek olarak kasten sanal yıkıcı önler) nesne dilimleme sorunları eğer senin türetilmiş sınıf, yeni bir durum ekler.

Genel olarak kalıtsal bir sınıfın halka açık bir sanal yıkıcı veya korumalı, sanal olmayan bir sınıfının olması gerekir. Gönderen C++ Coding Standardsbölüm 50:

50. Temel sınıf yıkıcılarını herkese açık ve sanal ya da korumalı ve sanal olmayan bir yere yerleştirin Silmek ya da silmemek; Soru şu: Eğer bir işaretçi ile bir üsse silme işlemine izin veriliyorsa, o zaman Base'in yıkıcısı açık ve sanal olmalıdır. Aksi takdirde, korunmalı ve sanal olmamalıdır.

C ++ 'nın dolaylı olarak vurgulama eğiliminde olduğu şeylerden biri (çünkü tasarımlar gerçekten kırılgan ve beceriksiz ve hatta başka türlü güvensiz olma eğilimindedir), kalıtımın bir düşünce olarak kullanılmak üzere tasarlanmış bir mekanizma olmadığı fikridir. Polimorfizm akılda tutulabilen bir genişletilebilirlik mekanizmasıdır, fakat genişletilebilirliğin gerektiği yere ilişkin öngörü gerektiren bir genişletme mekanizmasıdır. Sonuç olarak, temel sınıflarınız, önceden belirlenmiş bir miras hiyerarşisinin kökleri olarak tasarlanmalı ve önceden böyle bir öngörülemeden sonradan sonradan aldığınız bir şey değildir.

Mevcut kodu tekrar kullanmak için miras almak istediğiniz durumlarda, kompozisyon genellikle şiddetle tavsiye edilir (Kompozit Yeniden Kullanım Prensibi).


9

Neden c ++ tüm yıkıcıları varsayılan olarak sanal olarak ayarlamıyor? Ekstra depolama maliyeti ve sanal yöntem tablosu çağrısı. C ++, bunun düşük maliyetli olduğu yerlerde, düşük gecikmeli, rt programlama için kullanılır.


Yıkıcılar, zorlu gerçek zamanlı sistemlerde ilk etapta kullanılmamalı çünkü dinamik bellek gibi birçok kaynak güçlü son tarih garantisi sağlamak için kullanılamaz
Marco A.

9
@MarcoA. Yıkıcılar ne zamandan beri dinamik bellek tahsisini ima ediyor?
chbaker0

@ chbaker0 Ben bir 'like' kullandım. Onlar benim deneyimimde kullanılmıyorlar.
Marco A.

6
Ayrıca, dinamik belleğin zor gerçek zamanlı sistemlerde kullanılamaması saçmadır. Sabit tahsis boyutlarına ve tahsis bitmapine sahip önceden yapılandırılmış bir yığının, o bitmap'i taramak için harcadığı sürede ya bellek tahsis edeceğini ya da yetersiz bellek durumu getireceğini ispatlamak oldukça önemsizdir.
MSalters

Beni düşündüren bültenler: Her bir işlemin maliyetinin tür sisteminde depolandığı bir program hayal edin. Derleme zamanı gerçek zamanlı garantilerin denetlenmesine izin verilmesi.
Yakk

5

Bu, sanal imha cihazının ne zaman kullanılmayacağına güzel bir örnek: Scott Meyers'dan:

Bir sınıf herhangi bir sanal işlev içermiyorsa, bu genellikle temel sınıf olarak kullanılmadığını gösterir. Bir sınıfın temel sınıf olarak kullanılması amaçlanmadığında, yıkıcıyı sanal yapmak genellikle kötü bir fikirdir. ARM'deki bir tartışmaya dayanarak bu örneği düşünün:

// class for representing 2D points
class Point {
public:
    Point(short int xCoord, short int yCoord);
    ~Point();
private:
    short int x, y;
};

Kısa bir int 16 bit işgal ederse, bir Point nesnesi 32 bitlik bir sicile sığabilir. Ayrıca, bir Point nesnesi, C veya FORTRAN gibi diğer dillerde yazılmış işlevlere 32 bitlik bir miktar olarak geçirilebilir. Ancak Point'in yıkıcısı sanallaştırılırsa durum değişir.

Sanal üye eklediğiniz an, sınıfınıza, o sınıf için sanal tabloya işaret eden sanal bir işaretçi eklenir.


If a class does not contain any virtual functions, that is often an indication that it is not meant to be used as a base class.Wut. Başka hiç kimse, sanal yöntemlerle ilgilenmek zorunda kalmadan tekrar tekrar kullanılabilir üye ve davranış katmanları oluşturmak için sınıfları ve mirası kullanmamıza izin verilen Eski Eski Günleri hatırlıyor mu? Hadi Scott. Çekirdek noktayı anlıyorum, ama bu "sık sık" gerçekten ulaşıyor.
underscore_d

3

Sanal yıkıcı, çalışma zamanı maliyeti ekler. Sınıf, başka sanal yöntemlere sahip değilse, maliyet özellikle çok yüksektir. Sanal yıkıcı, yalnızca bir nesnenin silindiği veya bir imleç aracılığıyla bir temel sınıfa tahrip olduğu belirli bir senaryoda da gereklidir. Bu durumda, temel sınıf yıkıcısı sanal olmalı ve türetilmiş herhangi bir sınıfın yıkıcısı dolaylı olarak sanal olacaktır. Polimorfik bir temel sınıfın, yıkıcının sanal olması gerekmeyecek şekilde kullanıldığı birkaç senaryo vardır:

  • Türetilmiş sınıfların örnekleri öbek üzerine tahsis edilmemişse, örneğin sadece doğrudan istif üzerinde veya diğer nesnelerin içinde. (Başlatılmamış belleği ve yerleştirme operatörünü yeni kullanıyorsanız hariç).
  • Türetilmiş sınıfların örnekleri öbek üzerinde tahsis edilmişse, ancak silme yalnızca işaretçiler aracılığıyla en fazla türetilmiş sınıfa, örneğin bir var std::unique_ptr<Derived>, ve polimorfizm yalnızca sahip olmayan işaretçiler ve referanslarla gerçekleşir. Başka bir örnek ise, nesneler kullanılarak tahsis edilir std::make_shared<Derived>(). std::shared_ptr<Base>İlk işaretçi a olduğu sürece kullanmak iyidir std::shared_ptr<Derived>. Bunun nedeni, paylaşılan işaretçilerin, sanal bir temel sınıf yıkıcısına dayanmayan yıkıcılar (deleter) için kendi dinamik gönderimlerine sahip olmalarıdır.

Tabii ki, nesneleri sadece yukarıda belirtilen şekillerde kullanmaya yönelik herhangi bir kural kolayca kırılabilir. Bu nedenle, Herb Sutter'ın tavsiyesi her zamanki gibi geçerli olmaya devam ediyor: "Temel sınıf yıkıcılar ya genel ve sanal olmalı ya da korunmalı ve sanal olmamalıdır." Bu şekilde, bir kişi sanal imha edici olmayan bir temel sınıfa bir işaretçiyi silmeye çalışırsa, büyük olasılıkla derleme zamanında bir erişim ihlali hatası alır.

Sonra tekrar (genel) temel sınıflar olarak tasarlanmayan sınıflar var. Benim kişisel tavsiyem onları finalC ++ 11 veya daha üstü yapmak. Kare mandal olacak şekilde tasarlandıysa, şanslar yuvarlak mandal kadar iyi çalışmaz. Bu, temel sınıf ve türetilmiş sınıf arasında açık bir miras sözleşmesi yapma tercihim, NVI (sanal olmayan arayüz) tasarım deseni için, somut temel sınıflardan ziyade soyut için ve korunan üye değişkenlerin sıkıntım, diğer şeylerin yanı sıra ama bu görüşlerin bir dereceye kadar tartışmalı olduğunu biliyorum.


1

Bir yıkıcı ilan etmek virtual, ancak classmirasınızı almayı planladığınızda gereklidir . Genellikle standart kütüphanenin sınıfları (örneğin std::string) sanal bir yıkıcı sağlamaz ve bu nedenle alt sınıflandırma için uygun değildir.


3
Nedeni alt sınıflama + polimorfizm kullanımıdır. Sanal bir yıkıcı, yalnızca dinamik bir çözünürlüğe ihtiyaç duyulduğunda, bu bir referans / işaretçi / ana sınıfa ne olursa olsun aslında bir alt sınıf örneğine başvurabilir.
Michel Billaud

2
@ MichelleBillaud aslında sanal dtors olmadan polimorfizm olabilir. SADECE polimorfik silme için sanal bir dtor gereklidir, yani deletebir işaretçiyi temel sınıfa çağırmak .
chbaker0

1

Yapıcıda vtable'ı oluşturmak için bir ek masraf olacaktır (başka sanal işlevleriniz yoksa, bu durumda SORUNLU, ancak her zaman değil, sanal bir yıkıcıya sahip olmalısınız). Ve başka bir sanal fonksiyonunuz yoksa, bu, nesnesinizi, gerekenden daha büyük bir işaretçi boyutunda yapar. Açıkçası, artan boyut küçük nesneler üzerinde büyük bir etkiye sahip olabilir.

Oy hakkını almak için okunan fazladan bir hafıza var ve bundan sonra fonksiyon indirikatını çağırmak için, yıkıcı çağrıldığında sanal olmayan yıkıcıdan daha fazla yüklenen fonksiyonunu çağır. Ve elbette, sonuç olarak, yıkıcıya yapılan her çağrı için küçük bir ekstra kod üretildi. Bu, derleyicinin gerçek türü algılayamadığı durumlar içindir - gerçek türünü çıkarabildiği durumlarda, derleyici vtable'ı kullanmaz ancak doğrudan yıkıcıyı çağırır.

Sen gerektiğini bunun yaratılmasını, o zaman sanal yıkıcı ihtiyacımız olanı yazın bilen kod yerine başka bir kuruluş tarafından tahrip / oluşturulabilir eğer sınıf özellikle bir taban sınıf olarak amaçlanıyorsa sanal yıkıcı var.

Emin değilseniz, sanal yıkıcı kullanın. Bir sorun olarak ortaya çıkıyorsa, "doğru yıkıcı çağrılmaz" ın neden olduğu hatayı bulmaya çalışmaktan çok sanal bir sorun çıkarması daha kolaydır.

Kısacası , aşağıdaki durumlarda sanal bir yıkıcıya sahip olmamanız gerekir: 1. Herhangi bir sanal işleviniz yok. 2. Sınıftan türetmeyin ( finalC ++ 11 ile işaretleyin , derleyiciden türetmeye çalışırsanız bunu söyleyecektir).

Çoğu durumda, "çok fazla içerik" olmadıkça belirli bir nesneyi kullanarak harcanan zamanın önemli bir parçası değildir (1 MB'lık bir dize oluşturmak belli bir zaman alacaktır çünkü en az 1 MB veri gerekir. şu anda bulunduğu yerden kopyalanamaz). 1 MB'lık bir diziyi imha etmek, 150B dizenin imha edilmesinden daha kötü değildir, her ikisi de dize deposunun yeniden tahsis edilmesini gerektirir ve çok fazla değil, bu yüzden harcanan zaman genellikle aynıdır (bu bir hata ayıklama yapısı olmadığı sürece, yeniden tahsisatın genellikle belleği bir "zehir kalıbı" - ancak üretimdeki gerçek uygulamanızı nasıl yürüteceğiniz].

Kısacası, küçük bir ek yük var, ancak küçük nesneler için bir fark yaratabilir.

Ayrıca, derleyicilerin bazı durumlarda sanal aramayı en iyi duruma getirebileceğini unutmayın, bu nedenle yalnızca bir ceza

Her zaman olduğu gibi, performans, bellek ayak izi vb. İçin: Kıyaslama ve profil ve ölçüm, sonuçları alternatiflerle karşılaştırın ve zaman / belleğin EN ÇOK harcandığı yere bakın ve% 90'ını optimize etmeye çalışmayın. fazla çalışmayan kod [çoğu uygulama yürütme süresinde çok etkili olan kodun yaklaşık% 10'una ve hiç bir etkisi olmayan kodun% 90'ına sahiptir]. Bunu yüksek bir optimizasyon seviyesinde yapın, böylece derleyicinin iyi bir iş çıkarmasından faydalanabilirsiniz! Ve tekrarlayın, tekrar kontrol edin ve adım adım ilerleyin. Zeki olmaya çalışmayın ve neyin önemli olduğunu ve o tür bir uygulamada çok fazla deneyiminiz olmadıkça neyin olmadığını anlamaya çalışmayın.


1
"vlot oluşturmak için yapıcıda bir ek yük olacak" - vlot genellikle derleyici tarafından her bir sınıfa göre "yaratılmıştır", yapıcı sadece yapımcıya, yapım aşamasında nesne örneğinde kendisine bir işaretçi koyma yüküne sahip olacaktır.
Tony,

Buna ek olarak ... Ben tamamen erken optimizasyondan kaçınmak üzereyim, ama bunun tersine, You **should** have a virtual destructor if your class is intended as a base-classkaba bir aşırı basitleştirme - ve erken karamsarlık . Bu, yalnızca türetilmiş bir sınıfı göstericiyle taban tabanından silme izni varsa gereklidir. Pek çok durumda, öyle değil. Biliyorsanız, o zaman kesin, ek yükü doğuracak. Gerçek çağrılar derleyici tarafından statik olarak çözümlenebilse bile , btw, her zaman eklenir. Aksi takdirde, insanların nesnelerinizle neler yapabileceğini doğru bir şekilde kontrol ettiğinizde, buna değmez
underscore_d
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.