Türetilmiş sınıf işlevinden bir üst sınıf işlevi nasıl çağrılır?


603

C ++ kullanarak türetilmiş bir sınıftan üst işlevi nasıl çağırırım? Örneğin, denilen parentbir sınıf childve üst sınıftan türetilen bir sınıf var . Her sınıf içinde bir printişlev vardır. Çocuğun yazdırma işlevinin tanımında, ebeveynlerin yazdırma işlevine bir çağrı yapmak istiyorum. Bunu nasıl yapabilirim?


Yukarıdaki tüm çözümler, yazdırma işlevinizin statik bir yöntem olduğunu varsayar. Durum böyle mi? Yöntem statik değilse, yukarıdaki çözümler ilgili değildir.
hhafez

14
hhafez, base :: function () sözdizimi statik yöntem çağrısı sözdizimine benziyor, ancak bu bağlamda örneğin yöntemler için çalışıyor.
Motti

2
Platforma özel olduğu için MSVC __super kullanmam. Kodunuz başka bir platformda çalışmayabilir, ancak amaçlanan dil olarak yaptıkları için diğer önerileri kullanırdım.
Teaser


Türetilmiş sınıfların her zaman üst sınıf işlevlerini çağırmak için gerekli olduğu antipattern , Call super
Rufus

Yanıtlar:


775

Açık olanı belirtme riskini alırım: Fonksiyonu çağırırsınız, temel sınıfta tanımlanmışsa, türetilmiş sınıfta otomatik olarak kullanılabilir (eğer değilse private).

Türetilmiş sınıfta aynı imzayla bir işlev varsa, temel sınıfın adını ve ardından iki sütunu ekleyerek bu işlevi netleştirebilirsiniz base_class::foo(...). Java ve C # 'dan farklı olarak, C ++ ' nın temel sınıf "( veya ) için bir anahtar kelimesi olmadığını , çünkü C ++ belirsizliğe yol açabilecek birden fazla kalıtımı desteklediğini unutmayın .superbase

class left {
public:
    void foo();
};

class right {
public:
    void foo();
};

class bottom : public left, public right {
public:
    void foo()
    {
        //base::foo();// ambiguous
        left::foo();
        right::foo();

        // and when foo() is not called for 'this':
        bottom b;
        b.left::foo();  // calls b.foo() from 'left'
        b.right::foo();  // call b.foo() from 'right'
    }
};

Bu arada, aynı sınıftan iki kez doğrudan türetemezsiniz çünkü temel sınıflardan birine diğerine atıfta bulunmanın bir yolu olmayacaktır.

class bottom : public left, public left { // Illegal
};

31
Neden aynı sınıftan iki kez miras almak istiyorsunuz?
Paul Brewczynski

65
@bluesm: klasik OOP'de çok mantıklı değil, ancak genel programlamada template<class A, class B> class C: public A, public B {};, kodunuzun nasıl kullanıldığına (A ve B'yi aynı yapan) bağlı nedenlerden dolayı iki tür aynı olabilir, iki veya üç olabilir ne yaptığının farkında olmayan birinden soyutlama katmanı yolu.
Emilio Garavaglia

8
Ben bu o ana sınıfında doğrudan uygulanan olmasa bile üst sınıf yöntemini çağırmak edeceğini, eklemek kullanışlı bir yöntem olmakla düşünüyorum edilir miras zincirinde üst sınıflardan birinde uyguladı.
Maxim Lavrov

4
Bir cidenote, bunu bir cpp dosyasına koymaya çalıştığımda beni deli yaptı. 'Namespace std kullanıyorum' vardı. 'left' bu ad alanında bir yerde tanımlanır. Örnek derlenmeyecek - beni delirtti :). Sonra 'sol'u' Sol 'olarak değiştirdim. Bu arada harika bir örnek.
Mathai

72
@Mathai Ve bu yüzden kullanmanız gerekmiyor using namespace std.
JAB

193

Adlandırılmış üst sınıf Parentve adlandırılmış alt sınıf göz önüne alındığında Child, şöyle bir şey yapabilirsiniz:

class Parent {
public:
    virtual void print(int x);
}

class Child : public Parent {
    void print(int x) override;
}

void Parent::print(int x) {
    // some default behavior
}

void Child::print(int x) {
    // use Parent's print method; implicitly passes 'this' to Parent::print
    Parent::print(x);
}

ParentAnahtar kelimenin değil, sınıfın gerçek adı olduğunu unutmayın .


Baz çağrı aksi işlevi geçersiz kılma anlamı yok olurdu, diğer mantığıyla serpiştirilmiş olsaydı Tabii ki, bu sadece bu yüzden belki biraz var, faydalı olacaktır çok öz bir;)
underscore_d

1
@underscore_d aslında, temel çağrı diğer mantıkla serpiştirilmemiş olsa bile yararlıdır. Üst sınıfın hemen hemen istediğiniz her şeyi yaptığını varsayalım, ancak çocuk kullanıcılarının kullanmasını istemediğiniz bir foo () yöntemini ortaya koyar - ya foo () çocukta anlamsız olduğu için veya çocuğa dış arayanlar çocuğun ne olduğunu berbat edecektir yapıyor. Böylece çocuk parent :: foo () yöntemini belirli durumlarda kullanabilir ancak ebeveynin foo () işlevinin çağrılmasını önlemek için foo uygulaması sağlayabilir.
iheanyi

@iheanyi Kulağa ilginç geliyor, ama üzgünüm, henüz anlamadım. Mı foo()benzer buraya print()veya ayrı fonksiyon? Ve kullanarak demek privatetabanından miras gizlemek ayrıntılara miras ve temin publicsen şeyler için fonksiyonları gölgeleme yapmak sergilemek istediğiniz?
underscore_d

@underscore_d Evet, foo()benzerdi print(). print()Bu bağlamda daha anlamlı olacağını düşündüğüm için kullanmaya geri döneyim. Birisinin belirli bir veri türünde bazı işlemler gerçekleştiren, bazı erişimcileri ortaya çıkaran ve bir print(obj&)yöntemi olan bir sınıf oluşturduğunu varsayalım . Üzerinde çalışan yeni bir sınıfa ihtiyacım var array-of-objama diğer her şey aynı. Kompozisyon çok sayıda çoğaltılan kodla sonuçlanır. Devralma, print(array-of-obj&) döngü çağrısında print(obj&)bunu en aza indirir , ancak istemcilerin aramasını print(obj&)istemez, çünkü bunu yapmaları mantıklı değildir
iheanyi

@underscore_d Bu, orijinal ebeveyn sınıfının ortak kısımlarını yeniden düzenleyemediğim ya da bunu yapmanın inanılmaz derecede maliyetli olduğu varsayımına dayanıyor. Özel kalıtım işe yarayabilir, ancak daha sonra güvenmekte olduğunuz genel erişimcileri kaybedersiniz ve bu nedenle kodu çoğaltmanız gerekir.
iheanyi

32

Temel sınıfınız çağrılırsa Baseve işleviniz çağrılırsa , FooBar()doğrudanBase::FooBar()

void Base::FooBar()
{
   printf("in Base\n");
}

void ChildOfBase::FooBar()
{
  Base::FooBar();
}

28

MSVC'de bunun için Microsoft'a özel bir anahtar kelime vardır: __super


MSDN: Geçersiz kıldığınız bir işlev için temel sınıf uygulamasını çağırdığınızı açıkça belirtmenizi sağlar.

// deriv_super.cpp
// compile with: /c
struct B1 {
   void mf(int) {}
};

struct B2 {
   void mf(short) {}

   void mf(char) {}
};

struct D : B1, B2 {
   void mf(short) {
      __super::mf(1);   // Calls B1::mf(int)
      __super::mf('s');   // Calls B2::mf(char)
   }
};


5
Eh, typdefebeveyni böyle bir şey olarak tercih ederim super.
Thomas Eding

26
Ben kullanımını haklı göstermeyeceğim __super; Burada alternatif bir öneri olarak bahsettim. Geliştiriciler derleyicilerini bilmeli ve yeteneklerinin artılarını ve eksilerini anlamalıdır.
Andrey

13
Kodun taşınabilirliğini ciddi şekilde engellediğinden, kimsenin onu kullanmasını önermemeyi tercih ederim.
Erbureth, Reinstate Monica

26
Andrey ile aynı fikirde değilim: Geliştiriciler standardı bilmeli ve derleyici özellikleri ile uğraşmak zorunda kalmamalı, eğer öncelikle derleyiciden bağımsız olan iyi bir fikir olduğunu düşündüğüm bir yazılım yazmayı düşünürsek, er ya da geç büyük projelerde çoklu derleyiciler zaten kullanılıyor.
Gabriel

7
"Geliştiriciler derleyicilerini bilmeli" Bu muhakeme ve standart dışı özelliklerin dahil edilmesi, IE6 yol açtı ...
e2-e4

5

Temel sınıf üye işlevinin erişim değiştiricisi VEYA genel olarak korunuyorsa, türetilmiş sınıftan temel sınıfın çağrı üye işlevini yapabilirsiniz. Türetilmiş üye işlevinden temel olmayan sanal ve sanal üye işlevi çağrısı yapılabilir. Lütfen programa bakın.

#include<iostream>
using namespace std;

class Parent
{
  protected:
    virtual void fun(int i)
    {
      cout<<"Parent::fun functionality write here"<<endl;
    }
    void fun1(int i)
    {
      cout<<"Parent::fun1 functionality write here"<<endl;
    }
    void fun2()
    {

      cout<<"Parent::fun3 functionality write here"<<endl;
    }

};

class Child:public Parent
{
  public:
    virtual void fun(int i)
    {
      cout<<"Child::fun partial functionality write here"<<endl;
      Parent::fun(++i);
      Parent::fun2();
    }
    void fun1(int i)
    {
      cout<<"Child::fun1 partial functionality write here"<<endl;
      Parent::fun1(++i);
    }

};
int main()
{
   Child d1;
   d1.fun(1);
   d1.fun1(2);
   return 0;
}

Çıktı:

$ g++ base_function_call_from_derived.cpp
$ ./a.out 
Child::fun partial functionality write here
Parent::fun functionality write here
Parent::fun3 functionality write here
Child::fun1 partial functionality write here
Parent::fun1 functionality write here

1
Bazı örnekler getirdiğiniz için teşekkür ederiz virtual!
M.Ioan

5

Üst kapsam çözüm operatörü ile üst yöntemi çağırın.

Veli :: yöntemi ()

class Primate {
public:
    void whatAmI(){
        cout << "I am of Primate order";
    }
};

class Human : public Primate{
public:
    void whatAmI(){
        cout << "I am of Human species";
    }
    void whatIsMyOrder(){
        Primate::whatAmI(); // <-- SCOPE RESOLUTION OPERATOR
    }
};

-15
struct a{
 int x;

 struct son{
  a* _parent;
  void test(){
   _parent->x=1; //success
  }
 }_son;

 }_a;

int main(){
 _a._son._parent=&_a;
 _a._son.test();
}

Referans örneği.


2
Bu kodun soruyu neden / nasıl yanıtladığını açıklayarak lütfen düzenleyebilir misiniz ? Yalnızca kod yanıtları önerilmez, çünkü bir açıklama ile kod kadar kolay öğrenilemezler. Bir açıklama olmadan ne yapıldığını, kodda yapılan değişiklikleri veya kodun yararlı olup olmadığını anlamak çok daha fazla zaman ve çaba gerektirir. Açıklama, hem yanıttan öğrenmeye çalışanlar hem de cevabı değerlendirenlerin geçerli olup olmadığını veya oy vermeye değer olup olmadığını görmek için önemlidir.
Makyen

3
Bu soru türetilmiş sınıflarla ilgiliyken iç içe sınıflarla ilgilidir ('ebeveyn' ve 'çocuk' kelimeleri biraz yanıltıcı olsa da) ve bu nedenle soruyu hiç cevaplamamaktadır.
Johannes Matokic
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.