คำอธิบาย Virtual / pure virtual


346

มันหมายความว่าอะไรถ้าฟังก์ชั่นถูกกำหนดให้เป็นเสมือนจริงและมันก็เหมือนกับของแท้

คำตอบ:


339

จากฟังก์ชั่นเสมือนจริงของ Wikipedia ...

ในการเขียนโปรแกรมเชิงวัตถุในภาษาเช่น C ++ และ Object Pascal, ฟังก์ชั่นเสมือนหรือวิธีเสมือนเป็นฟังก์ชั่นหรือวิธีการที่สืบทอดได้และ overridable หรือวิธีการที่สะดวกในการจัดส่ง แนวคิดนี้เป็นส่วนสำคัญของ (polymorphism) ส่วน (รันไทม์) ของการเขียนโปรแกรมเชิงวัตถุ (OOP) ในระยะสั้นฟังก์ชั่นเสมือนจริงกำหนดฟังก์ชั่นเป้าหมายที่จะดำเนินการ แต่เป้าหมายอาจไม่เป็นที่รู้จักในเวลารวบรวม

แตกต่างจากฟังก์ชั่นที่ไม่ใช่เสมือนเมื่อฟังก์ชั่นเสมือนถูกแทนที่รุ่นที่ได้มามากที่สุดจะถูกใช้ในทุกระดับของลำดับชั้นของชั้นมากกว่าระดับที่มันถูกสร้างขึ้น ดังนั้นหากหนึ่งวิธีของคลาสพื้นฐานเรียกใช้เมธอดเสมือนเวอร์ชันที่กำหนดในคลาสที่ได้รับจะถูกใช้แทนเวอร์ชันที่กำหนดในคลาสพื้นฐาน

นี่คือตรงกันข้ามกับฟังก์ชั่นที่ไม่ใช่เสมือนซึ่งยังคงสามารถถูกแทนที่ในคลาสที่ได้รับ แต่รุ่น "ใหม่" จะถูกใช้โดยคลาสที่ได้รับและด้านล่างเท่านั้น แต่จะไม่เปลี่ยนฟังก์ชั่นของคลาสพื้นฐานเลย

ในขณะที่ ..

ฟังก์ชัน virtual pure หรือ pure virtual method เป็นฟังก์ชันเสมือนที่จำเป็นต้องมีการใช้งานโดยคลาสที่ได้รับหากคลาสที่ได้รับนั้นไม่เป็นนามธรรม

เมื่อมีเมธอดเสมือนจริงคลาสจะเป็น "นามธรรม" และไม่สามารถสร้างอินสแตนซ์ได้ด้วยตัวเอง คลาสที่ได้รับซึ่งใช้วิธี pure-virtual แทนต้องใช้แทน pure-virtual ไม่ได้ถูกกำหนดใน base-class เลยดังนั้นคลาสที่ได้รับจะต้องกำหนดมันหรือคลาสที่ได้รับนั้นเป็นนามธรรมและไม่สามารถสร้างอินสแตนซ์ได้ เฉพาะคลาสที่ไม่มีเมธอด abstract เท่านั้นที่สามารถสร้างอินสแตนซ์

เสมือนมีวิธีการแทนที่การทำงานของชั้นฐานและบริสุทธิ์เสมือนต้องใช้มัน


10
ดังนั้น ... คำหลักเสมือนจริงที่บริสุทธิ์หรือเพียงแค่คำที่ใช้?
Justin

197
Virtual void Function () = 0; เป็นเสมือนจริงที่บริสุทธิ์ "= 0" หมายถึงความบริสุทธิ์
Goz

8
Justin 'pure virtual' เป็นเพียงคำศัพท์ (ไม่ใช่คำหลักให้ดูคำตอบของฉันด้านล่าง) ใช้เพื่อหมายถึง "ฟังก์ชันนี้ไม่สามารถใช้งานได้โดยคลาสฐานดังที่ Goz กล่าวเพิ่ม" = 0 "ที่ส่วนท้ายของ virtual ฟังก์ชั่นทำให้มัน "บริสุทธิ์"
Nick Haddad

14
ฉันเชื่อว่า Stroustrup บอกว่าเขาต้องการเพิ่มpureคำหลัก แต่ Bell Labs กำลังจะเปิดตัว C ++ ครั้งใหญ่และผู้จัดการของเขาจะไม่อนุญาตในช่วงปลายปีนั้น การเพิ่มคำหลักเป็นเรื่องใหญ่
quark

14
นี่ไม่ใช่คำตอบที่ดี วิธีการใด ๆ สามารถ overriden ไม่ใช่แค่เสมือน ดูคำตอบของฉันสำหรับรายละเอียดเพิ่มเติม
Asik

212

ฉันต้องการแสดงความคิดเห็นเกี่ยวกับคำจำกัดความของวิกิพีเดียเสมือนจริงหลายครั้งที่นี่ [ในขณะที่คำตอบนี้ถูกเขียนขึ้น] Wikipedia ได้กำหนดวิธีเสมือนเป็นวิธีหนึ่งที่สามารถแทนที่ได้ในคลาสย่อย [โชคดีที่ Wikipedia ได้รับการแก้ไขตั้งแต่นั้นและตอนนี้อธิบายได้อย่างถูกต้อง] นั่นไม่ถูกต้อง: วิธีการใด ๆ ที่ไม่ใช่แค่เวอร์ชวลแมชีนสามารถถูกแทนที่ในคลาสย่อยได้ สิ่งที่เสมือนไม่สามารถที่จะทำให้คุณมีความแตกต่าง, ที่อยู่, ความสามารถในการเลือกที่ใช้เวลามากที่สุดแทนที่มาของวิธีการ

พิจารณารหัสต่อไปนี้:

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

ผลลัพธ์ของโปรแกรมนี้คืออะไร?

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

ได้รับมาแทนที่ทุกวิธีของ Base: ไม่เพียง แต่เสมือนจริง แต่ยังไม่ใช่เสมือน

เราเห็นว่าเมื่อคุณมี Base-pointer-to-Derived (bDerived) การโทร NonVirtual จะเรียกใช้งานคลาสฐาน สิ่งนี้ได้รับการแก้ไขในเวลาคอมไพล์เลอร์: คอมไพเลอร์เห็นว่า bDerived เป็น Base * ซึ่ง NonVirtual ไม่ใช่เสมือนดังนั้นจึงทำการแก้ไขบนคลาสฐาน

อย่างไรก็ตามการเรียก Virtual เรียกใช้งานคลาส Derived เนื่องจากคำหลักเสมือนการเลือกวิธีการจึงเกิดขึ้น ณรันไทม์ไม่ใช่การคอมไพล์เวลา สิ่งที่เกิดขึ้นที่นี่ในเวลาคอมไพล์เลอร์คือคอมไพเลอร์เห็นว่านี่คือ Base * และมันกำลังเรียกใช้เมธอดเสมือนดังนั้นจึงแทรกการเรียกไปยัง vtable แทน class base vtable นี้มีการสร้างอินสแตนซ์ ณ รันไทม์ดังนั้นการแก้ไขรันไทม์ไปยังการแทนที่ที่ได้รับมากที่สุด

ฉันหวังว่านี่จะไม่สับสนเกินไป ในระยะสั้นวิธีการใด ๆ ที่สามารถแทนที่ แต่เพียงวิธีการเสมือนให้ความแตกต่างนั่นคือการเลือกเวลาทำงานของการแทนที่ที่ได้รับมากที่สุด อย่างไรก็ตามในทางปฏิบัติการเอาชนะวิธีที่ไม่ใช่เสมือนนั้นถือว่าเป็นการปฏิบัติที่ไม่ดีและไม่ค่อยได้ใช้ดังนั้นหลาย ๆ คน (รวมถึงผู้ที่เขียนบทความ Wikipedia) คิดว่าวิธีเสมือนเท่านั้นที่สามารถถูกแทนที่ได้


6
เพียงเพราะบทความ Wikipedia (ซึ่งฉันไม่มีทางป้องกัน) กำหนดวิธีการเสมือน "เป็นหนึ่งที่สามารถแทนที่ใน subclasses" ไม่ได้ยกเว้นความเป็นไปได้ที่วิธีการอื่น ๆ ที่ไม่ใช่เสมือนสามารถประกาศชื่อเดียวกัน สิ่งนี้เรียกว่าโอเวอร์โหลด

26
คำจำกัดความนั้นไม่ถูกต้อง วิธีการที่สามารถถูกแทนที่ในคลาสที่ได้รับไม่ใช่เสมือนโดยการกำหนด ว่าวิธีการที่สามารถถูกแทนที่นั้นไม่เกี่ยวข้องกับคำจำกัดความของ "เสมือน" นอกจากนี้ "การโหลดมากเกินไป" มักจะหมายถึงการมีหลายวิธีที่มีชื่อและประเภทการส่งคืนเหมือนกัน แต่มีอาร์กิวเมนต์ต่างกันในคลาสเดียวกัน มันแตกต่างจาก "การเอาชนะ" ซึ่งหมายถึงลายเซ็นเดียวกัน แต่ในชั้นเรียนที่ได้รับ เมื่อมีการทำแบบไม่ polymorphically (ไม่ใช่ฐานเสมือน) ก็มักจะเรียกว่า "การซ่อน"
Asik

5
นี่ควรเป็นคำตอบที่ยอมรับได้ บทความวิกิพีเดียโดยเฉพาะซึ่งฉันจะใช้เวลาในการเชื่อมโยงที่นี่เนื่องจากไม่มีใครในคำถามนี้ได้ทำมันเป็นขยะสมบูรณ์ +1 ท่านดี
josaphatv

2
ตอนนี้มันสมเหตุสมผลแล้ว ขอบคุณครับท่านที่ดีสำหรับการอธิบายอย่างถูกต้องว่าวิธีการใด ๆ ที่สามารถแทนที่ได้ด้วยคลาสที่ได้รับและการเปลี่ยนแปลงคือวิธีที่คอมไพเลอร์จะทำหน้าที่เลือกฟังก์ชั่นที่เรียกว่าในสถานการณ์ที่แตกต่างกัน
Doodad

3
มันอาจจะเป็นประโยชน์ในการเพิ่ม a Derived*ด้วยการเรียกฟังก์ชั่นเดียวกันเพื่อขับรถกลับบ้าน คำตอบที่ยอดเยี่ยมเป็นอย่างอื่น
Jeff Jones

114

คำหลักเสมือนจริงให้ความสามารถของ C ++ ในการสนับสนุนความหลากหลาย เมื่อคุณมีตัวชี้ไปยังวัตถุของบางคลาสเช่น:

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

ในตัวอย่าง (โง่) นี้ฟังก์ชัน GetNumberOfLegs () จะส่งกลับตัวเลขที่เหมาะสมตามคลาสของวัตถุที่ถูกเรียกใช้

ตอนนี้ให้พิจารณาฟังก์ชั่น 'SomeFunction' มันไม่สนใจว่าวัตถุสัตว์ชนิดใดจะถูกส่งไปยังวัตถุนั้นตราบใดที่วัตถุนั้นได้มาจากสัตว์ คอมไพเลอร์จะส่งคลาสใด ๆ ที่ได้จาก Animal ไปเป็น Animal โดยอัตโนมัติเนื่องจากเป็นคลาสพื้นฐาน

ถ้าเราทำสิ่งนี้:

Duck d;
SomeFunction(&d);

มันจะเอาท์พุท '2' ถ้าเราทำสิ่งนี้:

Horse h;
SomeFunction(&h);

มันจะเอาท์พุท '4' เราทำสิ่งนี้ไม่ได้:

Animal a;
SomeFunction(&a);

เพราะมันจะไม่รวบรวมเพราะ GetNumberOfLegs () ฟังก์ชั่นเสมือนเป็นจริงซึ่งหมายความว่ามันจะต้องดำเนินการโดยได้รับคลาส (คลาสย่อย)

ฟังก์ชั่นเพียวเสมือนส่วนใหญ่จะใช้ในการกำหนด:

a) คลาสนามธรรม

เหล่านี้เป็นคลาสพื้นฐานที่คุณต้องได้รับจากพวกเขาและจากนั้นใช้ฟังก์ชั่นเสมือนจริง

b) อินเทอร์เฟซ

เหล่านี้เป็นคลาส 'เปล่า' ที่ฟังก์ชันทั้งหมดล้วนเป็นเสมือนจริงและด้วยเหตุนี้คุณต้องสืบทอดและใช้งานฟังก์ชันทั้งหมด


ในตัวอย่างของคุณคุณไม่สามารถทำ # 4 ได้เนื่องจากคุณไม่ได้เตรียมการใช้งานวิธีเสมือนจริง ไม่ใช่อย่างเคร่งครัดเพราะวิธีนี้เป็นเสมือนจริง
iheanyi

@iheanyi คุณไม่สามารถให้การปรับใช้กับวิธีเสมือนจริงในคลาสพื้นฐานได้ ดังนั้น # 4 ยังคงเป็นข้อผิดพลาด
ปราสาท

32

ในคลาส C ++ เสมือนเป็นคีย์เวิร์ดที่กำหนดว่าเมธอดสามารถถูกแทนที่ (เช่นถูกใช้โดย) คลาสย่อย ตัวอย่างเช่น:

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

ในกรณีนี้คลาสย่อยสามารถแทนที่ฟังก์ชันinitShapeเพื่อทำงานพิเศษบางอย่าง:

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

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

คำว่าpure virtualหมายถึงฟังก์ชั่นเสมือนที่ต้องดำเนินการโดยคลาสย่อยและไม่ได้นำไปใช้โดยคลาสฐาน คุณกำหนดวิธีการเป็นเสมือนจริงโดยใช้คำสำคัญเสมือนและเพิ่ม= 0ที่ส่วนท้ายของการประกาศวิธีการ

ดังนั้นหากคุณต้องการให้ Shape :: initShape pure virtual คุณจะทำสิ่งต่อไปนี้:

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

ด้วยการเพิ่มวิธีเสมือนจริงที่บริสุทธิ์ให้กับคลาสของคุณคุณทำให้คลาสเป็นคลาสพื้นฐานที่เป็นนามธรรม ซึ่งมีประโยชน์มากสำหรับการแยกอินเทอร์เฟซจากการใช้


1
เกี่ยวกับ "ฟังก์ชั่นเสมือนจริงที่ต้องดำเนินการโดยคลาสย่อย" - นั่นไม่ใช่ความจริงอย่างเคร่งครัด แต่คลาสย่อยนั้นเป็นนามธรรมถ้าไม่เป็นเช่นนั้น และคลาสนามธรรมไม่สามารถสร้างอินสแตนซ์ได้ นอกจากนี้ "ไม่สามารถนำไปใช้กับคลาสฐานได้" ดูเหมือนจะทำให้เข้าใจผิด ฉันขอแนะนำว่า "ยังไม่เคย" จะดีกว่าเนื่องจากไม่มีข้อ จำกัด ในการแก้ไขโค้ดเพื่อเพิ่มการใช้งานภายในคลาสพื้นฐาน
NVRAM

2
และ "ฟังก์ชั่น getName ไม่สามารถใช้งานได้โดยคลาสย่อย" ไม่ถูกต้องนัก คลาสย่อยสามารถใช้เมธอด (พร้อมลายเซ็นเดียวกันหรือแตกต่างกัน) แต่การนำไปใช้นั้นจะไม่เอาชนะเมธอด คุณสามารถใช้ Circle เป็นคลาสย่อยและใช้ "std :: string Circle :: getName ()" - จากนั้นคุณสามารถเรียกใช้เมธอดอย่างใดอย่างหนึ่งสำหรับอินสแตนซ์ของ Circle แต่ถ้าใช้ผ่านตัวชี้ Shape หรือการอ้างอิงคอมไพเลอร์จะเรียก Shape :: getName ()
NVRAM

1
คะแนนดีทั้งสองด้าน ฉันพยายามอยู่ห่างจากการพูดคุยกรณีพิเศษสำหรับตัวอย่างนี้ฉันจะแก้ไขคำตอบเพื่อให้อภัยมากขึ้น ขอบคุณ!
Nick Haddad

@NickHaddad ด้ายเก่า m_nameแต่สงสัยว่าทำไมคุณเรียกว่าตัวแปรของคุณ อะไรm_หมายถึง?
Tqn

1
@Tqn สมมติว่า NickHaddad ได้ปฏิบัติตามอนุสัญญา m_name เป็นแบบแผนการตั้งชื่อโดยทั่วไปเรียกว่าสัญกรณ์ฮังการี m หมายถึงสมาชิกของโครงสร้าง / คลาสจำนวนเต็ม
Ketcomp

16

"เสมือน" หมายความว่าวิธีนี้อาจถูกแทนที่ในคลาสย่อย แต่มีการใช้งานที่เรียกได้โดยตรงในคลาสฐาน "Pure virtual" หมายถึงเป็นวิธีเสมือนที่ไม่มีการติดตั้งใช้งานได้โดยตรง วิธีการดังกล่าวจะต้องถูกแทนที่อย่างน้อยหนึ่งครั้งในลำดับชั้นการสืบทอด - ถ้าชั้นมีวิธีเสมือนใด ๆ ที่ไม่ได้ดำเนินการวัตถุของชั้นนั้นไม่สามารถสร้างขึ้นได้และการรวบรวมจะล้มเหลว

@quark ชี้ให้เห็นว่าวิธีการเสมือนบริสุทธิ์สามารถมีการใช้งาน แต่เป็นวิธีการเสมือนบริสุทธิ์จะต้องถูกแทนที่การใช้งานเริ่มต้นไม่สามารถเรียกโดยตรง นี่คือตัวอย่างของวิธีการเสมือนจริงที่มีค่าเริ่มต้น:

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

ตามความเห็นการรวบรวมว่าจะล้มเหลวหรือไม่นั้นเป็นคอมไพเลอร์เฉพาะ อย่างน้อยใน GCC 4.3.3 จะไม่รวบรวม:

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

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

เอาท์พุท:

$ 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()

จะต้องถูกแทนที่ถ้าคุณต้องการสร้างอินสแตนซ์ของคลาส หากคุณไม่ได้สร้างอินสแตนซ์ใด ๆ รหัสจะรวบรวมได้
เกลน

1
การรวบรวมจะไม่ล้มเหลว หากไม่มีการใช้วิธีเสมือน (บริสุทธิ์) จากนั้นคลาส / วัตถุนั้นไม่สามารถสร้างอินสแตนซ์ได้ มันอาจจะไม่เชื่อมโยง แต่มันจะรวบรวม
ทิม

@Glen, @tim: คอมไพเลอร์ตัวไหน? เมื่อฉันพยายามคอมไพล์โปรแกรมที่สร้างคลาสนามธรรมมันไม่คอมไพล์
John Millikin

@John Compilation จะล้มเหลวหากคุณพยายามสร้างอินสแตนซ์ของคลาสที่มี PVF คุณสามารถสร้างอินสแตนซ์ของตัวชี้หรือค่าอ้างอิงสำหรับคลาสดังกล่าวได้

5
จอห์นสิ่งต่อไปนี้ไม่ถูกต้อง: "'Pure virtual' หมายถึงเป็นวิธีเสมือนที่ไม่มีการใช้งาน" วิธีการเสมือนบริสุทธิ์สามารถมีการใช้งาน แต่คุณไม่สามารถเรียกพวกเขาโดยตรง: คุณต้องลบล้างและใช้งานการใช้คลาสพื้นฐานจากภายในคลาสย่อย สิ่งนี้ช่วยให้คุณสามารถระบุส่วนเริ่มต้นของการใช้งานได้ มันไม่ใช่เทคนิคที่ใช้กันทั่วไป
quark

9

คำหลักเสมือนทำงานอย่างไร

สมมติว่าชายคนนั้นเป็นคนชั้นฐานอินเดียมาจากคน

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

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

การประกาศ do_work () เสมือนจริงหมายถึง: do_work () ที่จะเรียกจะถูกกำหนดในเวลาทำงานเท่านั้น

สมมติว่าฉันทำ

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

หากไม่ได้ใช้เสมือนจะมีการกำหนดแบบคงที่หรือผูกพันโดยคอมไพเลอร์ขึ้นอยู่กับสิ่งที่เรียกวัตถุ ดังนั้นถ้าวัตถุของ Man เรียก do_work (), man's do_work () จะถูกเรียกว่า EVEN THOUGH IT POINTS ไปยังวัตถุอินเดีย

ฉันเชื่อว่าคำตอบที่ได้รับการโหวตสูงสุดนั้นทำให้เข้าใจผิด - วิธีการใดก็ตามที่เสมือนจริงสามารถมีการใช้งานที่แทนที่ในชั้นเรียนที่ได้รับมา ด้วยการอ้างอิงถึง C ++ ความแตกต่างที่ถูกต้องคือเวลาทำงาน (เมื่อใช้เสมือน) การเชื่อมโยงและเวลาคอมไพล์ (เมื่อไม่ได้ใช้เสมือน แต่วิธีการนั้นถูกแทนที่และตัวชี้พื้นฐานจะชี้ไปที่วัตถุที่ได้รับ)

ดูเหมือนจะมีความคิดเห็นที่ทำให้เข้าใจผิดอีกว่า

"Justin, 'pure virtual' เป็นเพียงคำศัพท์ (ไม่ใช่คำหลักให้ดูคำตอบของฉันด้านล่าง) ใช้เพื่อหมายถึง" ฟังก์ชั่นนี้ไม่สามารถใช้งานได้โดยคลาสฐาน "

นี่มันผิด! ฟังก์ชั่นเสมือนล้วนๆสามารถมีร่างกายและสามารถนำไปใช้! ความจริงก็คือฟังก์ชั่นเสมือนจริงระดับนามธรรม 'สามารถเรียกได้แบบสแตติก! นักเขียนที่ดีสองคนคือ Bjarne Stroustrup และ Stan Lippman .... เพราะพวกเขาเขียนภาษา


2
น่าเสียดายที่เมื่อคำตอบเริ่มขึ้นแล้วผู้อื่นจะถูกเพิกเฉย แม้ว่าพวกเขาอาจจะดีกว่า
LtWorf

3

ฟังก์ชั่นเสมือนจริงเป็นฟังก์ชั่นสมาชิกที่มีการประกาศในคลาสฐานและที่กำหนดใหม่โดยคลาสที่ได้รับ ฟังก์ชันเสมือนเป็นลำดับชั้นตามลำดับการสืบทอด เมื่อคลาสที่ได้รับไม่ได้แทนที่ฟังก์ชันเสมือนฟังก์ชันที่กำหนดภายในคลาสพื้นฐานจะถูกใช้

ฟังก์ชั่นเสมือนที่แท้จริงคือสิ่งที่ไม่มีคำนิยามที่สัมพันธ์กับคลาสพื้นฐาน ไม่มีการนำไปใช้ในคลาสฐาน คลาสที่ได้รับใด ๆ จะต้องแทนที่ฟังก์ชันนี้


2

Simula, C ++ และ C # ซึ่งใช้การเชื่อมเมธอดสแตติกตามค่าเริ่มต้นโปรแกรมเมอร์สามารถระบุว่าเมธอดเฉพาะควรใช้การเชื่อมแบบไดนามิกโดยการทำเลเบลเป็นเสมือน การเชื่อมเมธอดแบบไดนามิกเป็นศูนย์กลางในการเขียนโปรแกรมเชิงวัตถุ

การเขียนโปรแกรมเชิงวัตถุต้องมีแนวคิดพื้นฐานสามประการ ได้แก่ การห่อหุ้มการสืบทอดและการเชื่อมโยงวิธีการแบบไดนามิก

การห่อหุ้มช่วยให้รายละเอียดการใช้งานของสิ่งที่เป็นนามธรรมถูกซ่อนอยู่หลังส่วนต่อประสานที่เรียบง่าย

การสืบทอดช่วยให้นามธรรมใหม่ถูกกำหนดเป็นส่วนขยายหรือการปรับแต่งของนามธรรมที่มีอยู่บางส่วนได้รับคุณสมบัติบางส่วนหรือทั้งหมดโดยอัตโนมัติ

การเชื่อมเมธอดแบบไดนามิกอนุญาตให้นามธรรมใหม่แสดงพฤติกรรมใหม่แม้ว่าจะใช้ในบริบทที่คาดว่าจะมีนามธรรมเก่า


1

เมธอดเสมือนสามารถถูกแทนที่ได้โดยการรับคลาส แต่ต้องการการนำไปใช้ในคลาสฐาน (อันที่จะแทนที่)

วิธีเสมือนบริสุทธิ์ไม่มีการใช้คลาสพื้นฐาน พวกเขาจำเป็นต้องกำหนดโดยคลาสที่ได้รับ (ดังนั้นการแทนที่ทางเทคนิคจึงไม่ใช่คำที่ถูกต้องเพราะไม่มีอะไรจะแทนที่)

Virtual สอดคล้องกับพฤติกรรมจาวาเริ่มต้นเมื่อคลาสที่ได้รับมาแทนที่เมธอดของคลาสฐาน

วิธีเพียวเสมือนสอดคล้องกับพฤติกรรมของวิธีนามธรรมภายในคลาสนามธรรม และคลาสที่มีเพียงเมธอดเสมือนจริงและค่าคงที่จะเป็น cpp-pendant ไปยังส่วนต่อประสาน


0

ฟังก์ชั่นเพียวเสมือน

ลองรหัสนี้

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

ใน class anotherClassลบฟังก์ชั่น sayHellow และเรียกใช้รหัส คุณจะได้รับข้อผิดพลาด! เนื่องจากเมื่อคลาสมีฟังก์ชันเสมือนบริสุทธิ์ไม่สามารถสร้างวัตถุจากคลาสนั้นและสืบทอดมาจากคลาสที่ได้รับนั้นต้องใช้ฟังก์ชันนั้น

ฟังก์ชั่นเสมือนจริง

ลองรหัสอื่น

#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!

}

นี่คือฟังก์ชั่น sayHellow ถูกทำเครื่องหมายเป็นเสมือนในคลาสพื้นฐานมันบอกว่าคอมไพเลอร์ที่พยายามค้นหาฟังก์ชั่นในชั้นเรียนที่ได้รับและใช้ฟังก์ชั่นหากไม่พบแล้วดำเนินการฐานหนึ่งขอบคุณ


ฮ่าฮ่าฉันใช้เวลา 30 วินาทีนานกว่าจะเข้าใจว่ามีอะไรผิดปกติที่นี่ ... HelloW :)
hans

0

"ฟังก์ชั่นเสมือนหรือวิธีเสมือนเป็นฟังก์ชั่นหรือวิธีการที่พฤติกรรมสามารถถูกแทนที่ในชั้นเรียนที่สืบทอดโดยฟังก์ชั่นที่มีลายเซ็นเดียวกัน" - วิกิพีเดีย

นี่ไม่ใช่คำอธิบายที่ดีสำหรับฟังก์ชั่นเสมือน เพราะแม้ว่าสมาชิกจะไม่เสมือนคลาสที่สืบทอดสามารถแทนที่ได้ คุณสามารถลองดูด้วยตัวคุณเอง

ความแตกต่างแสดงให้เห็นตัวเองเมื่อฟังก์ชั่นใช้คลาสฐานเป็นพารามิเตอร์ เมื่อคุณให้คลาสที่สืบทอดเป็นอินพุตฟังก์ชันนั้นใช้การใช้คลาสพื้นฐานของฟังก์ชัน overriden อย่างไรก็ตามถ้าฟังก์ชั่นนั้นเป็นเสมือนจริงมันจะใช้ฟังก์ชันที่นำมาใช้ในคลาสที่ได้รับมา


0
  • ฟังก์ชั่นเสมือนจริงจะต้องมีคำจำกัดความในคลาสฐานและในคลาสที่ได้รับมา แต่ไม่จำเป็นตัวอย่างเช่นฟังก์ชัน ToString () หรือ toString () เป็นเสมือนดังนั้นคุณสามารถให้การใช้งานของคุณเองโดยเอาชนะมันในคลาสที่ผู้ใช้กำหนด

  • ฟังก์ชั่นเสมือนจริงมีการประกาศและกำหนดไว้ในระดับปกติ

  • ฟังก์ชันเสมือนจริงต้องประกาศที่ลงท้ายด้วย "= 0" และสามารถประกาศได้ในคลาสนามธรรมเท่านั้น

  • คลาสนามธรรมที่มีฟังก์ชันเสมือนบริสุทธิ์ไม่สามารถมีคำจำกัดความของฟังก์ชันเสมือนบริสุทธิ์นั้นดังนั้นจึงมีความหมายว่าการใช้งานต้องมีให้ในคลาส (es) ที่ได้มาจากคลาสนามธรรมนั้น


โน้ตตัวเดียวกันกับที่จะ @rashedcs: แท้จริงฟังก์ชั่นเสมือนบริสุทธิ์สามารถมีความหมาย ...
Jarek C
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.