Yanıtlar:
Bir arayüz için daha da önemlidir. Sınıfınızın herhangi bir kullanıcısı, somut uygulamaya bir işaretçi değil, muhtemelen arabirime bir işaretçi tutacaktır. Silmeye geldiklerinde, yıkıcı sanal değilse, türetilmiş sınıfın yıkıcısını değil, arabirimin yıkıcısını (veya belirtmediyseniz derleyici tarafından sağlanan varsayılanı) çağırırlar. Anlık bellek sızıntısı.
Örneğin
class Interface
{
virtual void doSomething() = 0;
};
class Derived : public Interface
{
Derived();
~Derived()
{
// Do some important cleanup...
}
};
void myFunc(void)
{
Interface* p = new Derived();
// The behaviour of the next line is undefined. It probably
// calls Interface::~Interface, not Derived::~Derived
delete p;
}
[expr.delete]/
: ... if the static type of the object to be deleted is different from its dynamic type, ... the static type shall have a virtual destructor or the behavior is undefined. ...
. Derived'in dolaylı olarak oluşturulmuş bir yıkıcı kullanması hâlâ tanımsız olacaktır.
Sorunuzun cevabı genellikle, ancak her zaman değil. Soyut sınıfınız istemcilere bir işaretçide silme çağrısını yasaklarsa (veya belgelerinde böyle diyorsa), sanal bir yıkıcı bildirmemekte özgürsünüz.
İmha ediciyi korumalı hale getirerek istemcilerin bir işaretçide silme çağrısını yasaklayabilirsiniz. Bu şekilde çalışırken, sanal bir yıkıcıyı atlamak tamamen güvenli ve makul.
Sonunda sanal bir yöntem tablosu ile sonuçlanmayacak ve müşterilerinize bir işaretçi aracılığıyla silinemez olma niyetinizi bildireceksiniz, böylece bu durumlarda sanal olarak beyan etmemeniz için gerçekten nedeniniz var.
[Bu makaledeki 4. maddeye bakın: http://www.gotw.ca/publications/mill18.htm ]
Biraz araştırma yapmaya karar verdim ve cevaplarınızı özetlemeye çalıştım. Aşağıdaki sorular, ne tür bir yıkıcıya ihtiyacınız olduğuna karar vermenize yardımcı olacaktır:
Umarım bu yardımcı olur.
* C ++ 'da bir sınıfı nihai (alt sınıf dışı) olarak işaretlemenin bir yolu olmadığını belirtmek önemlidir, bu nedenle yıkıcıyı sanal olmayan ve herkese açık bir şekilde beyan etmeye karar vermeniz durumunda, diğer programcılarınızı karşı uyarmayı unutmayın. sınıfından türetmek.
Referanslar:
Evet her zaman önemlidir. Türetilmiş sınıflar, bellek tahsis edebilir veya nesne yok edildiğinde temizlenmesi gereken diğer kaynaklara başvuruda bulunabilir. Arabirimlerinize / soyut sınıflarınıza sanal yıkıcılar vermezseniz, türetilmiş bir sınıf örneğini temel sınıf tanıtıcısı ile her sildiğinizde, türetilmiş sınıfınızın yıkıcısı çağrılmaz.
Böylece, bellek sızıntıları potansiyelini açıyorsunuz
class IFoo
{
public:
virtual void DoFoo() = 0;
};
class Bar : public IFoo
{
char* dooby = NULL;
public:
virtual void DoFoo() { dooby = new char[10]; }
void ~Bar() { delete [] dooby; }
};
IFoo* baz = new Bar();
baz->DoFoo();
delete baz; // memory leak - dooby isn't deleted
Her zaman gerekli değildir , ancak iyi bir uygulama olduğunu düşünüyorum. Yaptığı şey, türetilmiş bir nesnenin temel tipteki bir işaretçi ile güvenli bir şekilde silinmesine izin vermesidir.
Yani mesela:
Base *p = new Derived;
// use p as you see fit
delete p;
Base
sanal bir yıkıcı yoksa , kötü biçimlendirilmiş , çünkü nesneyi a Base *
.
shared_ptr
nesneyi sanki a. silmeye çalışacaktır Base *
- yarattığınız şeyin türünü hatırlar. Referans verilen bağlantıya, özellikle de "Yıkıcı, T'nin sanal bir yıkıcıya sahip olmasa veya geçersiz olsa bile orijinal tipiyle birlikte aynı işaretçiyle silme çağrısı" der.
Bu sadece iyi bir uygulama değil. Herhangi bir sınıf hiyerarşisi için kural # 1'dir.
Şimdi Neden için. Tipik hayvan hiyerarşisini ele alalım. Sanal yıkıcılar, diğer herhangi bir yöntem çağrısında olduğu gibi sanal dağıtımdan geçer. Aşağıdaki örneği ele alalım.
Animal* pAnimal = GetAnimal();
delete pAnimal;
Hayvan'ın soyut bir sınıf olduğunu varsayalım. C ++ 'ın çağırmak için uygun yıkıcı bilmenin tek yolu sanal yöntem dağıtımıdır. Yıkıcı sanal değilse, Animal'in yıkıcısını çağırır ve türetilmiş sınıflardaki herhangi bir nesneyi yok etmez.
Yıkıcıyı temel sınıfta sanal yapmanın nedeni, seçimi türetilmiş sınıflardan kaldırmasıdır. Yıkıcıları varsayılan olarak sanal hale gelir.
Cevap basit, sanal olması gerekiyor, aksi takdirde temel sınıf tam bir polimorfik sınıf olmaz.
Base *ptr = new Derived();
delete ptr; // Here the call order of destructors: first Derived then Base.
Yukarıdaki silme işlemini tercih edersiniz, ancak taban sınıfın yıkıcısı sanal değilse, yalnızca taban sınıfın yıkıcısı çağrılır ve türetilmiş sınıftaki tüm veriler geri alınmaya devam eder.
delete p
tanımlanmamış davranışı başlatır. Araması garanti edilmezInterface::~Interface
.