Bisakah saya memanggil fungsi virtual kelas dasar jika saya menimpanya?

340

Katakanlah saya memiliki kelas Foodan Barmengatur seperti ini:

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;
    }
};

Seperti dijelaskan dalam kode, saya ingin dapat memanggil fungsi kelas dasar yang saya timpa. Di Jawa ada super.funcname()sintaksisnya. Apakah ini mungkin di C ++?

Alex
sumber
1
kemungkinan duplikat panggilan fungsi virtual dari kelas dasar
Vladimir F
1
Untuk Google: perhatikan bahwa Anda mungkin memiliki masalah seperti yang saya lakukan dengan menyimpannya sebagai variabel anggota kelas yang bukan pointer. Lihat jawaban saya di sini: stackoverflow.com/questions/4798966/... Saya terlibat baru / hapus untuk memperbaikinya.
Andrew

Jawaban:

449

Sintaks C ++ seperti ini:

class Bar : public Foo {
  // ...

  void printStuff() {
    Foo::printStuff(); // calls base class' function
  }
};
sth
sumber
11
Apakah ada masalah dengan melakukan hal ini? Apakah ini praktik yang buruk?
Robben_Ford_Fan_boy
36
@ David: Tidak, itu sangat normal untuk melakukan ini, meskipun mungkin tergantung pada kelas Anda yang sebenarnya jika itu benar-benar berguna. Panggil saja metode kelas dasar jika melakukan sesuatu yang Anda inginkan terjadi;).
sth
20
Hati-hati ini adalah bau kode ketika klien Anda DIPERLUKAN untuk melakukannya! (dipanggil call super requirement, lihat di sini: en.wikipedia.org/wiki/Call_super )
v.oddou
8
@ v.oddou: Tidak ada yang salah dengan memanggil kelas super saat ini berguna. Halaman yang Anda tautkan hanya tentang beberapa potensi masalah spesifik yang melibatkan pewarisan. Bahkan mengatakan sendiri bahwa tidak ada yang salah dengan memanggil kelas super secara umum: "Perhatikan bahwa itu adalah persyaratan memanggil orang tua yang merupakan anti-pola. Ada banyak contoh dalam kode nyata di mana metode dalam subkelas mungkin masih menginginkan fungsionalitas superclass, biasanya di mana ia hanya menambah fungsionalitas induknya. "
sth
26
Ini mungkin jelas bagi kebanyakan orang, tetapi untuk kelengkapan, ingatlah untuk tidak pernah melakukan ini dalam konstruktor dan destruktor.
TigerCoding
123

Iya,

class Bar : public Foo
{
    ...

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

Ini sama seperti superdi Jawa, kecuali itu memungkinkan memanggil implementasi dari basis yang berbeda ketika Anda memiliki banyak pewarisan.

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();
    }
};
Alex B
sumber
7
Ini adalah jawaban yang lebih baik daripada yang dipilih. Terima kasih.
Fisikawan Gila
Warisan berganda? Astaga!
lamino
69

Kadang-kadang Anda perlu memanggil implementasi kelas dasar, ketika Anda tidak dalam fungsi turunan ... Ini masih berfungsi:

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;
}
SelaluTraining
sumber
2
Perhatikan bahwa Anda harus xmengetik Base*dan menggunakan Base::Foosintaks agar berfungsi
TTimo
27

Kalau-kalau Anda melakukan ini untuk banyak fungsi di kelas Anda:

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

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

Ini mungkin menghemat sedikit penulisan jika Anda ingin mengganti nama Foo.

MartinStettner
sumber
1
Cara yang menyenangkan untuk melakukannya, tetapi tidak akan bekerja dengan banyak pewarisan.
Fisikawan Gila
5
Dan bagaimana jika Anda memiliki lebih dari 1 kelas dasar? Bagaimanapun, itu konyol dan seringkali sia-sia untuk mencoba membengkokkan C ++ agar terlihat seperti bahasa lain. Ingat saja nama pangkalan dan panggil dengan itu.
underscore_d
6

Jika Anda ingin memanggil fungsi kelas dasar dari kelas turunannya, Anda bisa memanggil fungsi overridden dengan menyebutkan nama kelas dasar (seperti Foo :: printStuff () ).

kode di sini

#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();
}

Sekali lagi Anda dapat menentukan pada saat runtime fungsi mana untuk memanggil menggunakan objek dari kelas itu (diturunkan atau basis). Tetapi ini membutuhkan fungsi Anda di kelas dasar harus ditandai sebagai virtual.

kode di bawah ini

#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();
}
Tunvir Rahman Tusher
sumber
0

Periksa ini...

#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;
}

keluaran

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

cara terbaik adalah menggunakan fungsi :: dasar sebagai say @sth

joseAndresGomezTovar
sumber
Seperti yang dijelaskan dalam Pertanyaan ini , ini seharusnya tidak berfungsi karena protectedwarisan. Untuk menggunakan pointer kelas dasar, Anda harus menggunakan warisan publik.
Darkproduct
Menarik. Setelah membaca jawaban ini saya pikir dengan pewarisan yang dilindungi fakta bahwa Turunkan berasal dari Base hanya akan terlihat oleh kelas itu sendiri dan tidak terlihat ke luar juga.
Darkproduct
0

Ya, Anda bisa menyebutnya. Sintaks C ++ untuk memanggil fungsi kelas induk di kelas anak adalah

class child: public parent {
  // ...

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

Baca lebih lanjut tentang penggantian fungsi .

Atif
sumber