Türetilmiş sınıflarda TÜM sanal işlevlerin uygulanması gerekir mi?


91

Bu basit bir soru gibi görünebilir, ancak cevabı başka hiçbir yerde bulamıyorum.

Aşağıdakilere sahip olduğumu varsayalım:

class Abstract {
public:
    virtual void foo() = 0;
    virtual void bar();
}

class Derived : Abstract {
public:
    virtual void foo();
}

Derived sınıfının bar () işlevini uygulamaması tamam mı? Ya türetilmiş sınıflarımın TÜMÜ bar () işlevine ihtiyaç duymuyorsa, ancak bazıları gerektiriyorsa. Soyut bir temel sınıfın tüm sanal işlevlerinin türetilmiş sınıflarda mı yoksa yalnızca tamamen sanal olanlarda mı uygulanması gerekir? Teşekkürler

Yanıtlar:


82

Türetilmiş sınıfları yapmak değil uygulamak zorunda tüm sanal fonksiyonları kendilerini. Sadece saf olanları uygulamaya ihtiyaçları var. 1 Bu Derived, sorudaki sınıfın doğru olduğu anlamına gelir . Bu devralırbar kendi atası sınıfından uygulanmasını Abstract. (Bu Abstract::bar, bir yerde uygulandığını varsayar . Sorudaki kod, yöntemi bildirir, ancak onu tanımlamaz. Bunu, Trenki'nin cevabının gösterdiği gibi satır içi tanımlayabilir veya ayrı olarak tanımlayabilirsiniz.)


1 Ve o zaman bile, sadece türetilmiş sınıfın somutlaştırılması gerekiyorsa . Türetilmiş sınıf doğrudan başlatılamaz ama sadece daha türetilmiş sınıfların bir temel sınıf olarak var değilse, o zaman var olan uygulamaya tüm saf sanal yöntemleri sahip sorumludur sınıflar. Hiyerarşideki "orta" sınıfın, temel sınıf gibi bazı saf sanal yöntemleri uygulanmadan bırakmasına izin verilir. "Orta" sınıfı ise yaptığı saf sanal yöntemi uygulamak onlar kendilerini yeniden uygulamak zorunda kalmamak için, daha sonra torunları, yani uygulanmasını devralır.


3
Ve hatta bu (saf sanal işlevlerin uygulanması), yalnızca (kendilerinin soyut bir temel sınıf olmalarının aksine) özdeşleştirilmeleri amaçlanıyorsa.
Christian Rau

1
İşte bu düşündüğüm şey. Ama bunu projemde yapıyorum ve Derived :: bar () için bir "çözülmemiş harici sembol" olduğunu söyleyen bir bağlantı hatası alıyorum; Ama Derived içinde asla bar beyan etmedim, öyleyse bağlayıcı neden işlev gövdesini arıyor?
mikestaub

1
@pixelpusher Tabii ki Derived::barbir işlev gövdesi var Abstract::bar. Öyleyse, tanımlandığı çeviri birimi (herhangi bir yerde tanımlanmış mı?), Çağrıldığı çeviri birimiyle bağlantılı değil gibi görünüyor.
Christian Rau

2
@Rob: They only need to implement the pure ones.Yanıltıcı. Türetilmiş sınıfları mutlaka yok gerek uygulamak için saf ya sanal fonksiyonlar.
Nawaz

Yardımın için minnettarım ama @trenki kafasına çivi çaktı. Tanımlanmadığı için, doğru Christian Rau da olmana rağmen.
mikestaub

47

Türetilmiş sınıflarda yalnızca saf sanal yöntemler uygulanmalıdır, ancak yine de diğer sanal yöntemlerin bir tanımına (ve yalnızca bir bildirimine değil) ihtiyacınız vardır. Bir tane sağlamazsanız, bağlayıcı çok iyi şikayet edebilir.

Dolayısıyla, {}isteğe bağlı sanal yönteminizin arkasına koymak size boş bir varsayılan uygulama sağlar:

class Abstract {
public:
    virtual void foo() = 0; // pure virtual must be overridden
    virtual void bar() {}   // virtual with empty default implementation
};

class Derived : Abstract {
public:
    virtual void foo();
};

Daha kapsamlı bir varsayılan uygulama, ayrı bir kaynak dosyaya gider.


7

ISO C ++ Standardı, saf sanal olmayan bir sınıfın tüm sanal yöntemlerinin tanımlanması gerektiğini belirtir.

Basitçe şu kuralı koyun:
Türetilmiş sınıfınız Base sınıfı sanal yöntemini geçersiz kılıyorsa, o zaman bir tanım da sağlamalıdır, değilse, o zaman Base sınıfı bu yöntemin tanımını sağlamalıdır.

Kod örneğinizdeki yukarıdaki kurala göre virtual void bar();, Base sınıfında bir tanıma ihtiyaç vardır.

Referans:

C ++ 03 Standart: 10.3 Sanal işlevler [class.virtual]

Bir sınıfta beyan edilen sanal bir işlev, o sınıfta veya her ikisinde de tanımlanmalı veya saf ilan edilmelidir (10.4); ancak teşhis gerekmez (3.2).

Yani ya işlevi tamamen sanal yapmalı ya da onun için bir tanım sağlamalısınız.

Gcc sss doccuments yanı o:

ISO C ++ Standardı, saf sanal olmayan bir sınıfın tüm sanal yöntemlerinin tanımlanması gerektiğini belirtir, ancak bu kuralın ihlalleri için herhangi bir tanı gerektirmez [class.virtual]/8. Bu varsayıma dayanarak, GCC yalnızca örtük olarak tanımlanmış kurucuları, atama işlecini, yıkıcıyı ve bu tür ilk satır içi olmayan yöntemi tanımlayan çeviri birimindeki bir sınıfın sanal tablosunu yayınlayacaktır.

Bu nedenle, bu belirli yöntemi tanımlayamazsanız, bağlayıcı, görünüşte ilgisiz semboller için tanımların eksikliğinden şikayet edebilir. Ne yazık ki, bu hata mesajını iyileştirmek için bağlayıcıyı değiştirmek gerekebilir ve bu her zaman yapılamaz.

Çözüm, saf olmayan tüm sanal yöntemlerin tanımlanmasını sağlamaktır. Bir yıkıcının, saf sanal olarak bildirilmiş olsa bile tanımlanması gerektiğini unutmayın [class.dtor]/7.


3

Evet, sorun değil ... soyut bir temel sınıftan türetilmiş bir sınıfı başlatmak için yalnızca herhangi bir saf sanal işlevi uygulamanız gerekir.


1

Evet, Türetilmiş bir sınıfın Üst Sınıfta Saf Sanal olan işlevi OVERRIDE olması gerektiği doğrudur. Saf Sanal İşleve sahip ebeveyn sınıfa, yalnızca Çocuk sınıfı Saf Sanal İşlevi kendi bedenlerini vermesi gerektiğinden Soyut Sınıf denir.

Normal Sanal İşlevler için: - Bazı alt sınıflar bu işleve sahip olabileceğinden, bazılarının sahip olmayabileceğinden, bunları daha fazla geçersiz kılmak gerekli değildir.

Sanal İşlev mekanizmasının temel amacı, Saf Sanal İşlevin (Soyut Sınıf) temel amacının kendi gövdesiyle İşlevle aynı ada sahip olmayı zorunlu kılmak olup olmadığına bakılmaksızın, Çalıştırma Süresi Polimorfizmidir.

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.