C ++ sanal işlevlerini güvenle geçersiz kılın


100

Sanal işleve sahip bir temel sınıfım var ve bu işlevi türetilmiş bir sınıfta geçersiz kılmak istiyorum. Derleyicinin türetilmiş sınıfta tanımladığım işlevin temel sınıftaki bir işlevi gerçekten geçersiz kılıp kılmadığını kontrol etmesini sağlamanın bir yolu var mı? Eskisini geçersiz kılmak yerine yanlışlıkla yeni bir işlevi ilan etmemeyi sağlayan bazı makro veya başka bir şey eklemek istiyorum.

Bu örneği ele alalım:

class parent {
public:
  virtual void handle_event(int something) const {
    // boring default code
  }
};

class child : public parent {
public:
  virtual void handle_event(int something) {
    // new exciting code
  }
};

int main() {
  parent *p = new child();
  p->handle_event(1);
}

Çocuğun yöntemi bildirimi kaçırdığı ve bu nedenle yeni bir yöntem bildirdiği için parent::handle_event()bunun yerine burada çağrılır . Bu, işlev adında bir yazım hatası veya parametre türlerinde bazı küçük farklar olabilir. Temel sınıfın arabirimi değişirse ve bir yerde bazı türetilmiş sınıflar değişikliği yansıtacak şekilde güncellenmezse de kolayca gerçekleşebilir.child::handle_event()const

Bu sorunu önlemenin bir yolu var mı, derleyiciye veya başka bir araca bunu benim için kontrol etmesini bir şekilde söyleyebilir miyim? Herhangi bir yararlı derleyici bayrağı (tercihen g ++ için)? Bu problemlerden nasıl kaçınıyorsunuz?


2
Harika soru, çocuğumun sınıf işlevinin neden bir saattir çağrılmadığını anlamaya çalışıyorum!
Akash Mankar

Yanıtlar:


89

G ++ 4.7'den beri, yeni C ++ 11 overrideanahtar sözcüğünü anlar :

class child : public parent {
    public:
      // force handle_event to override a existing function in parent
      // error out if the function with the correct signature does not exist
      void handle_event(int something) override;
};

@hirschhornsalz: handle_event işlevini uyguladığınızda ve işlev uygulamasının sonuna geçersiz kılma eklediğinizde, g ++ 'nın bir hata verdiğini gördüm; override anahtar sözcüğünü izleyen sınıf bildiriminde bir satır içi işlev uygulaması sağlarsanız, her şey yolunda demektir. Neden?
h9uest

3
Tanımda @ h9uest overridekullanılmalıdır. Bir satır içi uygulama hem tanım hem de uygulamadır, bu yüzden sorun değil.
Gunther Piez

@hirschhornsalz evet, g ++ ile aynı hata mesajını aldım. Yine de bir yan not: hem siz hem de g ++ error msg "sınıf tanımı" terimini kullandınız - "bildirim" ({bildirim, tanım} çifti) kullanmamalı mıyız? Bu bağlamda "tanım ve uygulama" diyerek kendinizi netleştirdiniz, ancak c ++ topluluğunun neden birdenbire sınıflardaki terimleri değiştirmeye karar verdiğini merak ediyorum?
h9uest

20

C # 'ın overrideanahtar sözcüğü gibi bir şey C ++ ' ın parçası değildir.

Gcc'de, -Woverloaded-virtualaynı ada sahip bir işleve sahip, ancak onu geçersiz kılmayacak kadar farklı bir imzaya sahip bir temel sınıf sanal işlevi gizlemeye karşı uyarır. Bununla birlikte, sizi işlev adının yanlış yazılması nedeniyle bir işlevi geçersiz kılmama konusunda korumaz.


2
Visual C ++ kullanıyorsanız
Steve Rowe

4
Visual C ++ kullanmak, C ++ 'da overridebir anahtar sözcük oluşturmaz; yine de bazı geçersiz C ++ kaynak kodunu derleyebilecek bir şey kullandığınız anlamına gelebilir. ;)
CB Bailey

3
Geçersiz kılmanın geçersiz C ++ olması, standardın yanlış olduğu ve Visual C ++ olmadığı anlamına gelir
Jon

2
@Jon: Tamam, şimdi neye gittiğini anladım. Şahsen C # tarzı overrideişlevselliği alabilir veya bırakabilirim ; Başarısız geçersiz kılmalarla nadiren sorun yaşadım ve bunların teşhis edilmesi ve düzeltilmesi nispeten kolaydı. Kabul etmeyeceğim bir düşünce, VC ++ kullanıcılarının bunu kullanması gerektiğidir. Belirli bir projenin taşınabilir olması gerekmese bile C ++ 'nın tüm platformlarda C ++ gibi görünmesini tercih ederim. It takımından C ++ 0x sahip olacağını belirtmek gerekir [[base_check]], [[override]]ve [[hiding]]sen istenirse kontrol geçersiz kılmak için tercih edebilirsiniz böylece bağlıyor.
CB Bailey 04

5
Tuhaftır, yorum sonra birkaç yıl VC ++ kullanarak yapmaz overridebir anahtar sözcük göründüğü gibi yaptılar . Uygun bir anahtar kelime değil, C ++ 11'de özel bir tanımlayıcı. Microsoft override, bununla ilgili özel bir durum oluşturmak ve öznitelikler için genel biçimi takip etmek için yeterince zorladı ve bunu standart haline getirdi :)
David Rodríguez - dribeas

18

Bildiğim kadarıyla, bunu soyut yapamaz mısın?

class parent {
public:
  virtual void handle_event(int something) const = 0 {
    // boring default code
  }
};

Www.parashift.com'da aslında soyut bir yöntem uygulayabileceğinizi okudum sanıyordum. Kişisel olarak bana mantıklı gelen bu, yaptığı tek şey alt sınıfları onu uygulamaya zorlamaktır, kimse bir uygulamanın kendisine sahip olmasına izin verilmediğinden bahsetmedi.


Ancak şimdi bunun yıkıcılardan daha fazlası üzerinde çalıştığını fark ettim! Harika bul.
strager

1
Bunun birkaç olası dezavantajı vardır: 1) bir veya daha fazla yöntemi soyut olarak işaretlemenin yaptığı diğer şey, temel sınıfı somutlaştırılamaz hale getirmektir; bu, sınıfın amaçlanan kullanımının bir parçası değilse bir sorun olabilir. 2) temel sınıf, ilk etapta değiştirmek için sizin olmayabilir.
Michael Burr

3
Michael Burr'a katılıyorum. Temel sınıf soyutlama yapmak sorunun bir parçası değil. Türetilmiş bir sınıfın geçersiz kılmasını istediğiniz, sanal bir yöntemde işlevselliğe sahip bir temel sınıfa sahip olmak tamamen mantıklıdır. Ve temel sınıftaki işlevi yeniden adlandıran ve türetilmiş sınıfınızın artık onu geçersiz kılmamasına neden olan başka bir programcıya karşı korunmak da aynı derecede mantıklıdır. Microsoft "geçersiz kılma" uzantısı bu durumda paha biçilemez. Bunun standarda eklendiğini görmeyi çok isterim, çünkü maalesef onsuz bunu yapmanın iyi bir yolu yok.
Brian

Bu aynı zamanda temel yöntemin (örneğin BaseClass::method()) türetilmiş uygulamada (örneğin DerivedClass::method()) çağrılmasını önler , örneğin bir varsayılan değer için.
Narcolessico

11

MSVC'de CLR'yi kullanabilirsiniz override olsanız bile anahtar sözcüğünü .

G ++ 'da, bunu her durumda uygulamanın doğrudan bir yolu yoktur; diğer insanlar, kullanarak imza farklılıklarının nasıl yakalanacağına dair iyi cevaplar verdiler -Woverloaded-virtual. Gelecekteki bir sürümde, birisi __attribute__ ((override))C ++ 0x sözdizimini kullanarak benzer veya eşdeğer bir sözdizimi ekleyebilir .


9

MSVC ++ 'da anahtar sözcük kullanabilirsinizoverride

class child : public parent {
public:
  virtual void handle_event(int something) <b>override</b> {
    // new exciting code
  }
};

override MSVC ++ 'da hem yerel hem de CLR kodu için çalışır.


5

Fonksiyonu soyut yapın, böylece türetilmiş sınıfların onu geçersiz kılmaktan başka seçeneği kalmaz.

@Ray Kodunuz geçersiz.

class parent {
public:
  virtual void handle_event(int something) const = 0 {
    // boring default code
  }
};

Soyut işlevlerin satır içi olarak tanımlanmış gövdeleri olamaz. Olmak için değiştirilmelidir

class parent {
public:
  virtual void handle_event(int something) const = 0;
};

void parent::handle_event( int something ) { /* do w/e you want here. */ }

3

Mantığınızda ufak bir değişiklik öneririm. Neyi başarmanız gerektiğine bağlı olarak işe yarayabilir veya çalışmayabilir.

handle_event () hala "sıkıcı varsayılan kodu" yapabilir, ancak sanal olmak yerine, "yeni heyecan verici kodu" yapmasını istediğiniz noktada temel sınıfın soyut bir yöntem çağırmasını sağlayın (yani geçersiz kılınması gereken) alt sınıfınız tarafından sağlanacaktır.

DÜZENLEME: Ve daha sonra alt sınıflarınızdan bazılarının "yeni heyecan verici kod" sağlamasına gerek olmadığına karar verirseniz , özeti sanal olarak değiştirebilir ve bu "eklenen" işlevselliğin boş bir temel sınıf uygulamasını sağlayabilirsiniz.


2

Derleyiciniz, bir temel sınıf işlevi gizlendiğinde üretebileceğine dair bir uyarı alabilir. Varsa etkinleştirin. Bu, parametre listelerindeki const çakışmalarını ve farklılıkları yakalayacaktır. Maalesef bu bir yazım hatasını ortaya çıkarmaz.

Örneğin, bu Microsoft Visual C ++ 'da C4263'ü uyarıyor.


1

C ++ 11 overrideanahtar sözcüğü türetilmiş sınıf içinde işlev bildirimi ile kullanıldığında, derleyiciyi bildirilen işlevin aslında bazı temel sınıf işlevlerini geçersiz kıldığını kontrol etmeye zorlar. Aksi takdirde, derleyici bir hata atar.

Bu nedenle, overridedinamik polimorfizmi (işlev geçersiz kılma) sağlamak için tanımlayıcıyı kullanabilirsiniz .

class derived: public base{
public:
  virtual void func_name(int var_name) override {
    // statement
  }
};
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.