Bagaimana cara memanggil fungsi kelas induk dari fungsi kelas turunan?


604

Bagaimana cara memanggil fungsi induk dari kelas turunan menggunakan C ++? Sebagai contoh, saya memiliki kelas yang disebut parent, dan kelas yang disebut childyang berasal dari orang tua. Di dalam setiap kelas ada printfungsi. Dalam definisi fungsi cetak anak, saya ingin melakukan panggilan ke fungsi cetak orang tua. Bagaimana saya bisa melakukan ini?


Semua solusi di atas mengasumsikan bahwa fungsi cetak Anda adalah metode statis. Apakah ini masalahnya? Jika metode ini tidak statis maka solusi di atas tidak relevan.
hhafez

14
hhafez, Anda salah sintaks base :: function () terlihat seperti sintaks pemanggilan metode statis tetapi berfungsi sebagai metode instance dalam konteks ini.
Motti

2
Saya tidak akan menggunakan __super MSVC karena ini spesifik platform. Meskipun kode Anda mungkin tidak berjalan di platform lain, saya akan menggunakan saran lain karena mereka melakukannya sesuai bahasa yang dimaksudkan.
Teaser


Antipattern di mana kelas turunan selalu diperlukan untuk memanggil fungsi kelas induk adalah Panggilan super
Rufus

Jawaban:


775

Saya akan mengambil risiko menyatakan yang sudah jelas: Anda memanggil fungsi, jika itu didefinisikan dalam kelas dasar itu secara otomatis tersedia di kelas turunan (kecuali jika itu private).

Jika ada fungsi dengan tanda tangan yang sama di kelas turunan, Anda dapat mendisambiguasinya dengan menambahkan nama kelas dasar diikuti oleh dua titik dua base_class::foo(...). Anda harus mencatat bahwa tidak seperti Java dan C #, C ++ tidak memiliki kata kunci untuk "kelas dasar" ( superatau base) karena C ++ mendukung multiple inheritance yang dapat menyebabkan ambiguitas.

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

Secara kebetulan, Anda tidak dapat memperoleh langsung dari kelas yang sama dua kali karena tidak akan ada cara untuk merujuk ke salah satu kelas dasar di atas yang lain.

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

31
Mengapa Anda ingin mewarisi dari kelas yang sama dua kali?
Paul Brewczynski

65
@ Bluesm: dalam OOP klasik tidak masuk akal, tetapi dalam pemrograman generik template<class A, class B> class C: public A, public B {};dapat datang ke dua jenis yang sama karena alasan tergantung pada bagaimana kode Anda digunakan (yang membuat A dan B menjadi sama), mungkin dua atau tiga lapisan abstraksi jauh dari seseorang yang tidak menyadari apa yang Anda lakukan.
Emilio Garavaglia

8
Saya pikir itu berguna untuk menambahkan, bahwa ini akan memanggil metode kelas induk bahkan jika tidak dilaksanakan secara langsung di kelas induk, tapi yang dilaksanakan di salah satu kelas induk dalam rantai warisan.
Maxim Lavrov

4
Pada sidenote, itu membuat saya marah ketika saya mencoba meletakkan ini dalam file cpp. Saya punya 'menggunakan namespace std'. 'kiri' didefinisikan di suatu tempat di namespace itu. Contoh tidak akan mengkompilasi - membuatku gila :). Kemudian saya mengubah 'kiri' menjadi 'Kiri'. Contoh yang bagus.
Mathai

72
@Mathai Dan itu sebabnya kamu tidak seharusnya menggunakan using namespace std.
JAB

193

Diberi nama kelas induk Parentdan kelas anak bernama Child, Anda dapat melakukan sesuatu seperti ini:

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

Perhatikan bahwa itu Parentadalah nama aktual kelas dan bukan kata kunci.


Tentu saja, ini hanya akan berguna jika panggilan dasar diselingi dengan logika lain, jika tidak akan ada gunanya mengesampingkan fungsi, jadi mungkin itu agak terlalu to-the-point;)
underscore_d

1
@underscore_d sebenarnya, ini berguna bahkan jika panggilan dasar tidak diselingi dengan logika lain. Katakanlah kelas induk cukup banyak melakukan semua yang Anda inginkan, tetapi mengekspos metode foo () yang tidak Anda inginkan untuk digunakan anak-anak - baik karena foo () tidak ada artinya pada anak atau penelepon eksternal ke anak akan mengacaukan apa anak itu perbuatan. Jadi anak dapat menggunakan parent :: foo () dalam situasi tertentu tetapi menyediakan implementasi foo sehingga mereka menyembunyikan foo () orang tua agar tidak dipanggil.
iheanyi

@iheanyi Kedengarannya menarik, tapi maaf, saya belum mengerti. Apakah di foo()sini analog dengan print()atau fungsi terpisah? Dan maksud Anda dengan menggunakan privatepewarisan untuk menyembunyikan detail yang diwarisi dari pangkalan, dan menyediakan publicfungsi bayangan untuk hal-hal yang ingin Anda paparkan?
underscore_d

@underscore_d Ya, foo()analog dengan print(). Biarkan saya kembali menggunakan print()karena saya pikir akan lebih masuk akal dalam konteks ini. Katakanlah seseorang membuat kelas yang melakukan beberapa set operasi pada tipe data tertentu, mengekspos beberapa accessor, dan memiliki print(obj&)metode. Saya membutuhkan kelas baru yang berfungsi array-of-objtetapi yang lainnya sama. Komposisi menghasilkan banyak kode duplikat. Warisan meminimalkan itu, dalam print(array-of-obj&) panggilan putaran print(obj&), tetapi tidak ingin klien memanggil print(obj&)karena tidak masuk akal bagi mereka untuk melakukannya
iheanyi

@underscore_d Ini didasarkan pada asumsi bahwa saya tidak dapat memperbaiki bagian-bagian umum dari kelas induk asli atau yang melakukannya sangat mahal. Warisan pribadi dapat berfungsi, tetapi kemudian Anda kehilangan aksesor publik yang Anda andalkan - dan karenanya, perlu menduplikasi kode.
iheanyi

32

Jika kelas dasar Anda dipanggil Base, dan fungsi FooBar()Anda dipanggil, Anda dapat memanggilnya langsung menggunakanBase::FooBar()

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

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

28

Di MSVC ada kata kunci khusus Microsoft untuk itu: __super


MSDN: Memungkinkan Anda untuk secara eksplisit menyatakan bahwa Anda memanggil implementasi kelas dasar untuk fungsi yang Anda timpa.

// 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, saya lebih suka typdeforang tua sebagai sesuatu seperti super.
Thomas Eding

26
Saya tidak akan mencoba membenarkan penggunaan __super; Saya sebutkan di sini sebagai saran alternatif. Pengembang harus mengetahui kompiler mereka dan memahami pro dan kontra dari kemampuannya.
Andrey

13
Saya lebih suka mencegah siapa pun menggunakannya, karena sangat menghalangi portabilitas kode.
Erbureth berkata Reinstate Monica

26
Saya tidak setuju dengan Andrey: Pengembang harus mengetahui standar dan tidak perlu repot dengan fitur kompiler, jika kami mempertimbangkan untuk menulis perangkat lunak yang terutama kompiler independen yang menurut saya adalah ide yang bagus karena cepat atau lambat dalam proyek-proyek besar banyak kompiler bagaimanapun digunakan.
Gabriel

7
"Pengembang harus tahu kompiler mereka" alasan ini, dan penyertaan fitur non standar, adalah apa yang menyebabkan IE6 ...
e2-e4

5

Jika pengubah akses fungsi anggota kelas dasar dilindungi ATAU publik, Anda dapat melakukan fungsi panggilan anggota kelas dasar dari kelas turunan. Panggilan ke kelas dasar fungsi anggota non-virtual dan virtual dari fungsi anggota turunan dapat dibuat. Silakan merujuk program.

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

Keluaran:

$ 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
Terima kasih telah membawa beberapa contoh virtual!
M.Ioan

5

Panggil metode induk dengan operator resolusi lingkup induk.

Induk :: metode ()

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

Contoh referensi.


2
Bisakah Anda mengedit penjelasan mengapa / bagaimana kode ini menjawab pertanyaan? Kode-saja jawaban tidak disarankan, karena mereka tidak mudah dipelajari dari sebagai kode dengan penjelasan. Tanpa penjelasan, dibutuhkan lebih banyak waktu dan upaya untuk memahami apa yang sedang dilakukan, perubahan yang dilakukan pada kode, atau jika kode tersebut berguna. Penjelasan itu penting baik bagi orang yang berusaha belajar dari jawaban dan mereka yang mengevaluasi jawaban untuk melihat apakah itu valid, atau layak untuk dipilih.
Makyen

3
Jawaban ini adalah tentang kelas bertingkat sedangkan pertanyaannya adalah tentang kelas turunan (meskipun kata 'orang tua' dan 'anak' agak menyesatkan) dan karenanya tidak menjawab pertanyaan sama sekali.
Johannes Matokic
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.