Geçersiz kılıyorsam bir temel sınıfın sanal işlevini çağırabilir miyim?


340

Diyelim ki derslerim var Foove şöyle Barayarladım:

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
        std::cout << x << std::endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        // I would like to call Foo.printStuff() here...
        std::cout << y << std::endl;
    }
};

Kodda açıklandığı gibi, temel sınıfın işlevini geçersiz kıldığım şekilde çağırmak istiyorum. Java'da super.funcname()sözdizimi vardır. Bu C ++ ile mümkün mü?



1
Google çalışanları için: bir işaretçi olmayan bir sınıf üyesi değişkeni olarak saklarken yaptığım gibi sorunlarınız olabileceğini unutmayın. Cevabımı burada görebilirsiniz: stackoverflow.com/questions/4798966/… Düzeltmek için yeni / delete ekledim.
Andrew

Yanıtlar:


449

C ++ sözdizimi şöyle:

class Bar : public Foo {
  // ...

  void printStuff() {
    Foo::printStuff(); // calls base class' function
  }
};

11
Bunu yapmakla ilgili olası bir sorun var mı? Kötü uygulama mu?
Robben_Ford_Fan_boy

36
@David: Hayır, bunu yapmak normaldir, ancak gerçekten faydalıysa gerçek sınıfınıza bağlı olabilir. Temel sınıf yöntemini yalnızca olmasını istediğiniz bir şey yaparsa çağırın;).
sth

20
dikkatli olun bu istemcilerin bunu GEREKİR hale geldiğinde bir kod kokusu ! (denir call super requirement, buradan kontrol edin: en.wikipedia.org/wiki/Call_super )
v.oddou

8
@ v.oddou: Yararlı olduğunda süper sınıfı çağırmanın yanlış bir yanı yok. Bağladığınız sayfa, kalıtımla ilgili belirli bir potansiyel sorunla ilgilidir. Hatta genel olarak süper sınıfı çağırmanın yanlış bir şey olmadığını söyler: " Anti-desen olan ebeveyni çağırmanın bir gereği olduğunu unutmayın . Gerçek kodda, alt
STH

26
Çoğu için açık olabilir, ancak tamlık için bunu asla yapıcılarda ve yıkıcılarda yapmayı unutmayın.
TigerCoding

123

Evet,

class Bar : public Foo
{
    ...

    void printStuff()
    {
        Foo::printStuff();
    }
};

superJava ile aynıdır , ancak birden fazla mirasınız olduğunda farklı bazlardan çağrılara izin verir.

class Foo {
public:
    virtual void foo() {
        ...
    }
};

class Baz {
public:
    virtual void foo() {
        ...
    }
};

class Bar : public Foo, public Baz {
public:
    virtual void foo() {
        // Choose one, or even call both if you need to.
        Foo::foo();
        Baz::foo();
    }
};

7
Bu, seçilenden daha iyi bir cevaptır. Teşekkürler.
Çılgın Fizikçi

Birden fazla miras? Ooh yikes!
lamino

69

Bazen, türetilmiş işlevde olmadığınızda temel sınıfın uygulamasını çağırmanız gerekir ... Hala çalışıyor:

struct Base
{
    virtual int Foo()
    {
        return -1;
    }
};

struct Derived : public Base
{
    virtual int Foo()
    {
        return -2;
    }
};

int main(int argc, char* argv[])
{
    Base *x = new Derived;

    ASSERT(-2 == x->Foo());

    //syntax is trippy but it works
    ASSERT(-1 == x->Base::Foo());

    return 0;
}

2
Hem xtipte olmanız hem de bunun çalışması Base*için Base::Foosözdizimini kullanmanız gerektiğini unutmayın
TTimo

27

Sınıfınızdaki birçok işlev için bunu yapmanız durumunda:

class Foo {
public:
  virtual void f1() {
    // ...
  }
  virtual void f2() {
    // ...
  }
  //...
};

class Bar : public Foo {
private:
  typedef Foo super;
public:
  void f1() {
    super::f1();
  }
};

Foo olarak yeniden adlandırmak istiyorsanız, bu biraz yazma tasarrufu sağlayabilir.


1
Bunu yapmanın eğlenceli yolu, ancak birden fazla miras ile çalışmaz.
Çılgın Fizikçi

5
Peki ya 1'den fazla temel sınıfınız varsa? Her neyse, başka bir dil gibi görünmek için C ++ 'ı bükmeye çalışmak aptalca ve genellikle nafiledir. Sadece üssün adını hatırlayın ve buna seslenin.
underscore_d

6

Temel sınıfın bir işlevini türetilmiş sınıftan çağırmak istiyorsanız, temel sınıf adından ( Foo :: printStuff () gibi ) bahsederek yalnızca geçersiz kılınan işlevin içinde çağrı yapabilirsiniz .

kod buraya gidiyor

#include <iostream>
using namespace std;

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
         cout<<"Base Foo printStuff called"<<endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        cout<<"derived Bar printStuff called"<<endl;
        Foo::printStuff();/////also called the base class method
    }
};

int main()
{
    Bar *b=new Bar;
    b->printStuff();
}

Yine çalışma zamanında hangi sınıfın nesnesini (türetilmiş veya temel) kullanarak çağrılacak işlevi belirleyebilirsiniz.Ancak bu, temel sınıftaki işlevinizin sanal olarak işaretlenmesi gerektiğini gerektirir.

aşağıdaki kod

#include <iostream>
using namespace std;

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
         cout<<"Base Foo printStuff called"<<endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        cout<<"derived Bar printStuff called"<<endl;
    }
};

int main()
{

    Foo *foo=new Foo;
    foo->printStuff();/////this call the base function
    foo=new Bar;
    foo->printStuff();
}

0

şuna göz at...

#include <stdio.h>

class Base {
public:
   virtual void gogo(int a) { printf(" Base :: gogo (int) \n"); };    
   virtual void gogo1(int a) { printf(" Base :: gogo1 (int) \n"); };
   void gogo2(int a) { printf(" Base :: gogo2 (int) \n"); };    
   void gogo3(int a) { printf(" Base :: gogo3 (int) \n"); };
};

class Derived : protected Base {
public:
   virtual void gogo(int a) { printf(" Derived :: gogo (int) \n"); };
   void gogo1(int a) { printf(" Derived :: gogo1 (int) \n"); };
   virtual void gogo2(int a) { printf(" Derived :: gogo2 (int) \n"); };
   void gogo3(int a) { printf(" Derived :: gogo3 (int) \n"); };       
};

int main() {
   std::cout << "Derived" << std::endl;
   auto obj = new Derived ;
   obj->gogo(7);
   obj->gogo1(7);
   obj->gogo2(7);
   obj->gogo3(7);
   std::cout << "Base" << std::endl;
   auto base = (Base*)obj;
   base->gogo(7);
   base->gogo1(7);
   base->gogo2(7);
   base->gogo3(7);

   std::string s;
   std::cout << "press any key to exit" << std::endl;
   std::cin >> s;
   return 0;
}

çıktı

Derived
 Derived :: gogo (int)
 Derived :: gogo1 (int)
 Derived :: gogo2 (int)
 Derived :: gogo3 (int)
Base
 Derived :: gogo (int)
 Derived :: gogo1 (int)
 Base :: gogo2 (int)
 Base :: gogo3 (int)
press any key to exit

en iyi yolu base :: işlevini say @sth olarak kullanmaktır


Açıklandığı gibi bu Soru , bunun nedeni eseri olmamalı protectedmiras. Temel sınıf işaretçisini yayınlamak için genel miras kullanmanız gerekir.
Darkproduct

İlginç. Bu cevabı okuduktan sonra korumalı kalıtımla düşündüm ki Derived'in Base'den türetilmesi sadece sınıfın kendisi tarafından görülebilir ve dışarıdan da görülemez.
Darkproduct

0

Evet diyebilirsiniz. Alt sınıftaki üst sınıf işlevini çağırmak için C ++ sözdizimi

class child: public parent {
  // ...

  void methodName() {
    parent::methodName(); // calls Parent class' function
  }
};

İşlev geçersiz kılma hakkında daha fazla bilgi edinin .

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.