Virtual / virtual murni dijelaskan

346

Apa sebenarnya artinya jika suatu fungsi didefinisikan sebagai virtual dan apakah itu sama dengan virtual murni?

Justin
sumber

Jawaban:

339

Dari fungsi Virtual Wikipedia ...

Dalam pemrograman berorientasi objek, dalam bahasa seperti C ++, dan Object Pascal, fungsi virtual atau metode virtual adalah fungsi atau metode yang dapat diwariskan dan dapat ditimpa untuk pengiriman dinamis yang difasilitasi. Konsep ini merupakan bagian penting dari bagian (runtime) polimorfisme pemrograman berorientasi objek (OOP). Singkatnya, fungsi virtual mendefinisikan fungsi target untuk dieksekusi, tetapi target mungkin tidak diketahui pada waktu kompilasi.

Tidak seperti fungsi non-virtual, ketika fungsi virtual ditimpa, versi yang paling diturunkan digunakan di semua tingkatan hirarki kelas, bukan hanya pada tingkat di mana ia dibuat. Oleh karena itu jika satu metode kelas dasar memanggil metode virtual, versi yang ditentukan dalam kelas turunan akan digunakan sebagai pengganti versi yang didefinisikan dalam kelas dasar.

Ini berbeda dengan fungsi non-virtual, yang masih dapat ditimpa dalam kelas turunan, tetapi versi "baru" hanya akan digunakan oleh kelas turunan dan di bawahnya, tetapi tidak akan mengubah fungsi dari kelas dasar sama sekali.

sedangkan..

Fungsi virtual murni atau metode virtual murni adalah fungsi virtual yang harus diterapkan oleh kelas turunan jika kelas turunannya tidak abstrak.

Ketika metode virtual murni ada, kelasnya "abstrak" dan tidak bisa dipakai sendiri. Sebagai gantinya, kelas turunan yang mengimplementasikan metode virtual-virtual harus digunakan. Pure-virtual sama sekali tidak didefinisikan di kelas dasar, jadi kelas turunan harus mendefinisikannya, atau kelas turunannya juga abstrak, dan tidak dapat dipakai. Hanya kelas yang tidak memiliki metode abstrak yang dapat dipakai.

Virtual menyediakan cara untuk mengesampingkan fungsionalitas kelas dasar, dan virtual-murni membutuhkannya .

Diego Dias
sumber
10
Jadi ... apakah virtual murni kata kunci, atau hanya istilah yang digunakan?
Justin
197
virtual void Function () = 0; adalah virtual murni. "= 0" menunjukkan kemurnian.
Goz
8
Justin, 'virtual murni' hanyalah sebuah istilah (bukan kata kunci, lihat jawaban saya di bawah) yang dulu berarti "fungsi ini tidak dapat diimplementasikan oleh kelas dasar. Seperti kata Goz, menambahkan" = 0 "di akhir virtual fungsi membuatnya "murni"
Nick Haddad
14
Saya percaya Stroustrup mengatakan bahwa ia ingin menambahkan purekata kunci, tetapi Bell Labs akan membuat rilis besar C ++, dan manajernya tidak mengizinkannya pada tahap akhir itu. Menambahkan kata kunci adalah masalah besar.
quark
14
Ini bukan jawaban yang bagus. Metode apa pun bisa diganti, bukan hanya yang virtual. Lihat jawaban saya untuk lebih jelasnya.
Asik
212

Saya ingin mengomentari definisi Wikipedia tentang virtual, seperti yang diulang oleh beberapa di sini. [Pada saat jawaban ini ditulis,] Wikipedia mendefinisikan metode virtual sebagai salah satu yang dapat ditimpa dalam subkelas. [Untungnya, Wikipedia telah diedit sejak itu, dan sekarang menjelaskan hal ini dengan benar.] Itu tidak benar: metode apa pun, tidak hanya yang virtual, dapat ditimpa dalam subkelas. Apa yang dilakukan virtual adalah memberi Anda polimorfisme, yaitu, kemampuan untuk memilih saat run-override metode yang paling diturunkan .

Pertimbangkan kode berikut:

#include <iostream>
using namespace std;

class Base {
public:
    void NonVirtual() {
        cout << "Base NonVirtual called.\n";
    }
    virtual void Virtual() {
        cout << "Base Virtual called.\n";
    }
};
class Derived : public Base {
public:
    void NonVirtual() {
        cout << "Derived NonVirtual called.\n";
    }
    void Virtual() {
        cout << "Derived Virtual called.\n";
    }
};

int main() {
    Base* bBase = new Base();
    Base* bDerived = new Derived();

    bBase->NonVirtual();
    bBase->Virtual();
    bDerived->NonVirtual();
    bDerived->Virtual();
}

Apa output dari program ini?

Base NonVirtual called.
Base Virtual called.
Base NonVirtual called.
Derived Virtual called.

Berasal menimpa setiap metode Base: tidak hanya yang virtual, tetapi juga non-virtual.

Kami melihat bahwa ketika Anda memiliki Base-pointer-to-Derived (bDerived), memanggil panggilan NonVirtual implementasi kelas Base. Ini diselesaikan pada waktu kompilasi: kompilator melihat bahwa bDerived adalah Basis *, bahwa NonVirtual bukan virtual, sehingga ia melakukan resolusi pada basis kelas.

Namun, memanggil panggilan Virtual implementasi kelas turunan. Karena kata kunci virtual, pemilihan metode terjadi pada saat run-time , bukan waktu kompilasi. Apa yang terjadi di sini pada waktu kompilasi adalah bahwa kompiler melihat bahwa ini adalah Basis *, dan itu memanggil metode virtual, sehingga memasukkan panggilan ke vtable, bukan Basis kelas. Vtable ini dipakai pada saat run-time, maka resolusi run-time ke override yang paling diturunkan.

Saya harap ini tidak terlalu membingungkan. Singkatnya, metode apa pun dapat diganti, tetapi hanya metode virtual yang memberi Anda polimorfisme, yaitu pemilihan run-time dari penggantian yang paling diturunkan. Namun dalam praktiknya, mengganti metode non-virtual dianggap praktik buruk dan jarang digunakan, sehingga banyak orang (termasuk siapa pun yang menulis artikel Wikipedia) berpikir bahwa hanya metode virtual yang dapat ditimpa.

Asik
sumber
6
Hanya karena artikel Wikipedia (yang saya tidak bisa pertahankan) mendefinisikan metode virtual "sebagai salah satu yang dapat ditimpa dalam subkelas" tidak mengecualikan kemungkinan bahwa metode lain, non-virtual, dengan nama yang sama dapat dideklarasikan. Ini dikenal sebagai overloading.
26
Definisi ini tetap salah. Metode yang dapat ditimpa dalam kelas turunan tidak virtual menurut definisi; apakah metode dapat ditimpa tidak relevan dengan definisi "virtual". Juga, "overloading" biasanya mengacu pada memiliki beberapa metode dengan nama yang sama dan tipe pengembalian tetapi argumen yang berbeda, di kelas yang sama; ini sangat berbeda dari "override" yang menyiratkan tanda tangan yang persis sama tetapi dalam kelas turunan. Ketika dilakukan secara non-polimorfik (basis non-virtual), ini sering disebut "bersembunyi".
Asik
5
Ini harus menjadi jawaban yang diterima. Artikel Wikipedia tertentu yang akan saya luangkan waktu untuk tautkan di sini karena tidak ada orang lain dalam pertanyaan ini yang melakukannya , benar-benar sampah. +1, tuan yang baik.
josaphatv
2
SEKARANG masuk akal. Terima kasih, tuan yang baik, untuk menjelaskan dengan benar bahwa metode apa pun dapat ditimpa oleh kelas turunan dan perubahannya adalah pada bagaimana kompiler akan berperilaku untuk memilih fungsi apa yang disebut dalam situasi yang berbeda.
Doodad
3
Mungkin bermanfaat untuk menambahkan Derived*dengan panggilan fungsi yang sama untuk mengarahkan titik pulang. Jika tidak, jawaban yang bagus
Jeff Jones
114

Kata kunci virtual memberi C ++ kemampuannya untuk mendukung polimorfisme. Ketika Anda memiliki pointer ke objek dari beberapa kelas seperti:

class Animal
{
  public:
    virtual int GetNumberOfLegs() = 0;
};

class Duck : public Animal
{
  public:
     int GetNumberOfLegs() { return 2; }
};

class Horse : public Animal
{
  public:
     int GetNumberOfLegs() { return 4; }
};

void SomeFunction(Animal * pAnimal)
{
  cout << pAnimal->GetNumberOfLegs();
}

Dalam contoh (konyol) ini, fungsi GetNumberOfLegs () mengembalikan angka yang sesuai berdasarkan kelas objek yang dipanggil.

Sekarang, pertimbangkan fungsi 'SomeFunction'. Tidak peduli apa jenis objek hewan yang diteruskan padanya, asalkan itu berasal dari Hewan. Compiler akan secara otomatis mengirimkan kelas turunan Animal ke Animal karena kelas dasar.

Jika kita melakukan ini:

Duck d;
SomeFunction(&d);

itu akan menghasilkan '2'. Jika kita melakukan ini:

Horse h;
SomeFunction(&h);

itu akan menghasilkan '4'. Kami tidak bisa melakukan ini:

Animal a;
SomeFunction(&a);

karena itu tidak akan dikompilasi karena fungsi virtual GetNumberOfLegs () menjadi murni, yang berarti harus diimplementasikan dengan menurunkan kelas (subclass).

Fungsi Virtual Murni sebagian besar digunakan untuk mendefinisikan:

a) kelas abstrak

Ini adalah kelas dasar di mana Anda harus mengambil darinya dan kemudian mengimplementasikan fungsi virtual murni.

b) antarmuka

Ini adalah kelas 'kosong' di mana semua fungsi adalah virtual murni dan karenanya Anda harus menurunkan dan kemudian mengimplementasikan semua fungsi.

JBRWilkinson
sumber
Dalam contoh Anda, Anda tidak dapat melakukan # 4 karena Anda tidak memberikan implementasi metode virtual murni. Ini tidak sepenuhnya karena metode ini murni virtual.
iheanyi
@iheanyi Anda tidak dapat menyediakan implementasi ke metode virtual murni di kelas dasar. Karenanya kasus # 4 masih error.
prasad
32

Dalam kelas C ++, virtual adalah kata kunci yang menunjukkan bahwa, suatu metode dapat diganti (yaitu diimplementasikan oleh) sebuah subkelas. Sebagai contoh:

class Shape 
{
  public:
    Shape();
    virtual ~Shape();

    std::string getName() // not overridable
    {
      return m_name;
    }

    void setName( const std::string& name ) // not overridable
    {
      m_name = name;
    }

  protected:
    virtual void initShape() // overridable
    {
      setName("Generic Shape");
    }

  private:
    std::string m_name;
};

Dalam hal ini, subclass dapat mengesampingkan fungsi initShape untuk melakukan beberapa pekerjaan khusus:

class Square : public Shape
{
  public: 
    Square();
    virtual ~Square();

  protected:
    virtual void initShape() // override the Shape::initShape function
    {
      setName("Square");
    }
}

Istilah virtual murni mengacu pada fungsi virtual yang perlu diimplementasikan oleh subclass dan belum diimplementasikan oleh kelas dasar. Anda menetapkan metode sebagai virtual murni dengan menggunakan kata kunci virtual dan menambahkan a = 0 pada akhir deklarasi metode.

Jadi, jika Anda ingin membuat Shape :: initShape pure virtual Anda akan melakukan hal berikut:

class Shape 
{
 ...
    virtual void initShape() = 0; // pure virtual method
 ... 
};

Dengan menambahkan metode virtual murni ke kelas Anda, Anda membuat kelas menjadi kelas dasar abstrak yang sangat berguna untuk memisahkan antarmuka dari implementasi.

Nick Haddad
sumber
1
Mengenai "fungsi virtual yang harus diimplementasikan oleh subclass" - itu tidak sepenuhnya benar, tetapi subclass juga abstrak jika tidak. Dan kelas abstrak tidak bisa dipakai. Juga, "tidak dapat diimplementasikan oleh kelas dasar" tampaknya menyesatkan; Saya menyarankan bahwa "belum" akan lebih baik karena tidak ada batasan untuk modifikasi kode untuk menambahkan implementasi dalam kelas dasar.
NVRAM
2
Dan "fungsi getName tidak dapat diimplementasikan oleh subclass" tidak tepat. Subclass dapat mengimplementasikan metode (dengan tanda tangan yang sama atau berbeda) tetapi implementasi tersebut tidak akan MENGATASI metode ini. Anda bisa mengimplementasikan Circle sebagai subclass dan mengimplementasikan "std :: string Circle :: getName ()" - lalu Anda bisa memanggil metode apa pun untuk instance Circle. Tetapi jika digunakan melalui pointer Shape atau referensi kompiler akan memanggil Shape :: getName ().
NVRAM
1
Poin bagus di kedua lini. Saya mencoba untuk tidak membahas kasus-kasus khusus untuk contoh ini, saya akan memodifikasi jawabannya menjadi lebih pemaaf. Terima kasih!
Nick Haddad
@NickHaddad Utas lama, tetapi bertanya-tanya mengapa Anda memanggil variabel Anda m_name. Apa m_artinya?
Tqn
1
@Tqn dengan asumsi NickHaddad telah mengikuti konvensi, m_name adalah konvensi penamaan yang biasa disebut notasi Hongaria. M menunjukkan anggota struktur / kelas, integer.
Ketcomp
16

"Virtual" berarti bahwa metode tersebut dapat ditimpa dalam subkelas, tetapi memiliki implementasi yang dapat dipanggil langsung di kelas dasar. "Pure virtual" berarti ini adalah metode virtual tanpa implementasi yang dapat dipanggil langsung. Metode seperti itu harus ditimpa setidaknya satu kali dalam hierarki warisan - jika suatu kelas memiliki metode virtual yang tidak diterapkan, objek dari kelas itu tidak dapat dibangun dan kompilasi akan gagal.

@quark menunjukkan bahwa metode virtual murni dapat memiliki implementasi, tetapi karena metode virtual murni harus ditimpa, implementasi default tidak dapat dipanggil secara langsung. Berikut adalah contoh metode virtual murni dengan default:

#include <cstdio>

class A {
public:
    virtual void Hello() = 0;
};

void A::Hello() {
    printf("A::Hello\n");
}

class B : public A {
public:
    void Hello() {
        printf("B::Hello\n");
        A::Hello();
    }
};

int main() {
    /* Prints:
           B::Hello
           A::Hello
    */
    B b;
    b.Hello();
    return 0;
}

Menurut komentar, apakah kompilasi akan gagal atau tidak adalah khusus kompiler. Setidaknya dalam GCC 4.3.3, itu tidak akan dikompilasi:

class A {
public:
    virtual void Hello() = 0;
};

int main()
{
    A a;
    return 0;
}

Keluaran:

$ g++ -c virt.cpp 
virt.cpp: In function int main()’:
virt.cpp:8: error: cannot declare variable a to be of abstract type A
virt.cpp:1: note:   because the following virtual functions are pure within A’:
virt.cpp:3: note:   virtual void A::Hello()
John Millikin
sumber
itu harus ditimpa jika Anda ingin membuat instance instance dari kelas. Jika Anda tidak membuat instance apa pun maka kode akan dikompilasi dengan baik.
Glen
1
kompilasi tidak akan gagal. Jika tidak ada implementasi metode virtual (murni) maka kelas / objek tidak dapat dipakai. Mungkin tidak LINK, tetapi akan dikompilasi.
Tim
@ Glen, @tim: di mana kompiler? Ketika saya mencoba mengkompilasi program yang membangun kelas abstrak, itu tidak mengkompilasi.
John Millikin
@John Compilation hanya akan gagal jika Anda mencoba untuk membuat instance instance dari kelas yang berisi PVF. Tentu saja Anda dapat membuat instantiate pointer atau nilai referensi untuk kelas tersebut.
5
Juga, John, yang berikut ini tidak tepat: "'Virtual murni' berarti itu adalah metode virtual tanpa implementasi." Metode virtual murni dapat memiliki implementasi. Tetapi Anda tidak dapat memanggil mereka secara langsung: Anda harus mengganti dan menggunakan implementasi kelas dasar dari dalam sub-kelas. Ini memungkinkan Anda untuk memberikan bagian default dari implementasi. Ini bukan teknik yang umum.
quark
9

Bagaimana cara kerja kata kunci virtual?

Asumsikan bahwa Manusia adalah kelas dasar, India berasal dari manusia.

Class Man
{
 public: 
   virtual void do_work()
   {}
}

Class Indian : public Man
{
 public: 
   void do_work()
   {}
}

Mendeklarasikan do_work () sebagai virtual berarti: do_work () yang akan dipanggil HANYA ditentukan pada saat run-time.

Misalkan saya lakukan,

Man *man;
man = new Indian();
man->do_work(); // Indian's do work is only called.

Jika virtual tidak digunakan, hal yang sama ditentukan secara statis atau terikat secara statis oleh kompiler, tergantung pada objek yang dipanggil. Jadi, jika sebuah objek dari Man memanggil do_work (), do_work () Man disebut EVEN THOUGH POINTS TO OBJECT INDIAN

Saya percaya bahwa jawaban yang dipilih adalah menyesatkan - Setiap metode apakah virtual dapat memiliki implementasi yang ditimpa di kelas turunan. Dengan referensi spesifik ke C ++ perbedaan yang benar adalah run-time (ketika virtual digunakan) mengikat dan mengkompilasi-waktu (ketika virtual tidak digunakan tetapi metode ditimpa dan penunjuk basis diarahkan ke objek yang diturunkan) mengikat fungsi terkait.

Tampaknya ada komentar menyesatkan lainnya yang mengatakan,

"Justin, 'virtual murni' hanyalah sebuah istilah (bukan kata kunci, lihat jawaban saya di bawah) yang dulu berarti" fungsi ini tidak dapat diimplementasikan oleh kelas dasar. "

INI SALAH! Fungsi virtual murni juga dapat memiliki tubuh DAN DAPAT DITERAPKAN! Yang benar adalah bahwa fungsi virtual murni kelas abstrak dapat disebut secara statis! Dua penulis yang sangat baik adalah Bjarne Stroustrup dan Stan Lippman .... karena mereka menulis bahasa.

McMurdo
sumber
2
Sayangnya begitu sebuah jawaban mulai diunggulkan, semua yang lain akan diabaikan. Bahkan mereka bisa lebih baik.
LtWorf
3

Fungsi virtual adalah fungsi anggota yang dideklarasikan dalam kelas dasar dan yang didefinisikan ulang oleh kelas turunan. Fungsi virtual bersifat hierarkis sesuai urutan warisan. Ketika kelas turunan tidak mengesampingkan fungsi virtual, fungsi yang didefinisikan dalam kelas dasarnya digunakan.

Fungsi virtual murni adalah yang tidak mengandung definisi relatif terhadap kelas dasar. Tidak memiliki implementasi di kelas dasar. Setiap kelas turunan harus menimpa fungsi ini.

ruam
sumber
2

Simula, C ++, dan C #, yang menggunakan pengikatan metode statis secara default, pemrogram dapat menentukan bahwa metode tertentu harus menggunakan pengikatan dinamis dengan memberi labelnya sebagai virtual. Pengikatan metode dinamis adalah pusat pemrograman berorientasi objek.

Pemrograman berorientasi objek membutuhkan tiga konsep dasar: enkapsulasi, pewarisan, dan pengikatan metode dinamis.

Enkapsulasi memungkinkan detail implementasi abstraksi disembunyikan di balik antarmuka yang sederhana.

Warisan memungkinkan abstraksi baru untuk didefinisikan sebagai perpanjangan atau penyempurnaan dari beberapa abstraksi yang ada, memperoleh beberapa atau semua karakteristiknya secara otomatis.

Pengikatan metode dinamis memungkinkan abstraksi baru untuk menampilkan perilaku barunya bahkan ketika digunakan dalam konteks yang mengharapkan abstraksi lama.

PJT
sumber
1

Metode virtual DAPAT ditimpa oleh menurunkan kelas, tetapi membutuhkan implementasi di kelas dasar (yang akan ditimpa)

Metode virtual murni tidak memiliki implementasi kelas dasar. Mereka perlu didefinisikan oleh kelas turunan. (Jadi secara teknis diganti bukan istilah yang tepat, karena tidak ada yang menimpanya).

Virtual berhubungan dengan perilaku java default, ketika kelas turunan menimpa metode kelas dasar.

Metode Virtual murni sesuai dengan perilaku metode abstrak dalam kelas abstrak. Dan kelas yang hanya berisi metode virtual murni dan konstanta akan menjadi cpp-pendant ke sebuah Interface.

johannes_lalala
sumber
0

Fungsi Virtual Murni

coba kode ini

#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{

public:

    virtual void sayHellow()=0;

};

class anotherClass:aClassWithPureVirtualFunction
{

public:

    void sayHellow()
    {

        cout<<"hellow World";
    }

};
int main()
{
    //aClassWithPureVirtualFunction virtualObject;
    /*
     This not possible to create object of a class that contain pure virtual function
    */
    anotherClass object;
    object.sayHellow();
}

Di kelas anotherClass menghapus fungsi sayHellow dan menjalankan kode. Anda akan mendapatkan kesalahan! Karena ketika sebuah kelas berisi fungsi virtual murni, tidak ada objek yang dapat dibuat dari kelas itu dan itu diwarisi maka kelas turunannya harus mengimplementasikan fungsi itu.

Fungsi virtual

coba kode lain

#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{

public:

    virtual void sayHellow()
    {
        cout<<"from base\n";
    }

};

class anotherClass:public aClassWithPureVirtualFunction
{

public:

    void sayHellow()
    {

        cout<<"from derived \n";
    }

};
int main()
{
    aClassWithPureVirtualFunction *baseObject=new aClassWithPureVirtualFunction;
    baseObject->sayHellow();///call base one

    baseObject=new anotherClass;
    baseObject->sayHellow();////call the derived one!

}

Di sini fungsi sayHellow ditandai sebagai virtual di kelas dasar. Dikatakan kompiler yang mencoba mencari fungsi di kelas turunan dan mengimplementasikan fungsinya. Jika tidak ditemukan maka jalankan basisnya. Terima kasih

Tunvir Rahman Tusher
sumber
Haha, butuh waktu 30 detik untuk memahami apa yang salah di sini ... HelloW :)
hans
0

"Fungsi virtual atau metode virtual adalah fungsi atau metode yang perilakunya dapat ditimpa dalam kelas pewarisan oleh fungsi dengan tanda tangan yang sama" - wikipedia

Ini bukan penjelasan yang bagus untuk fungsi virtual. Karena, bahkan jika anggota bukan virtual, mewarisi kelas dapat menimpanya. Anda dapat mencoba dan melihatnya sendiri.

Perbedaannya menunjukkan dirinya sendiri ketika suatu fungsi mengambil kelas dasar sebagai parameter. Saat Anda memberikan kelas pewarisan sebagai input, fungsi itu menggunakan implementasi kelas dasar dari fungsi overriden. Namun, jika fungsi itu virtual, ia menggunakan salah satu yang diimplementasikan dalam kelas turunan.

bisa
sumber
0
  • Fungsi virtual harus memiliki definisi dalam kelas dasar dan juga dalam kelas turunan tetapi tidak perlu, misalnya fungsi ToString () atau toString () adalah Virtual sehingga Anda dapat menyediakan implementasi Anda sendiri dengan menimpanya di kelas yang ditentukan pengguna.

  • Fungsi virtual dideklarasikan dan didefinisikan dalam kelas normal.

  • Fungsi virtual murni harus dideklarasikan diakhiri dengan "= 0" dan itu hanya dapat dideklarasikan dalam kelas abstrak.

  • Kelas abstrak yang memiliki fungsi virtual murni tidak dapat memiliki definisi fungsi virtual murni itu, sehingga ini menyiratkan bahwa implementasi harus disediakan di kelas yang berasal dari kelas abstrak itu.

Sohail xIN3N
sumber
Catatan yang sama dengan @rashedcs: Memang fungsi virtual murni dapat memiliki definisi ...
Jarek C