มันหมายความว่าอะไรถ้าฟังก์ชั่นถูกกำหนดให้เป็นเสมือนจริงและมันก็เหมือนกับของแท้
มันหมายความว่าอะไรถ้าฟังก์ชั่นถูกกำหนดให้เป็นเสมือนจริงและมันก็เหมือนกับของแท้
คำตอบ:
จากฟังก์ชั่นเสมือนจริงของ Wikipedia ...
ในการเขียนโปรแกรมเชิงวัตถุในภาษาเช่น C ++ และ Object Pascal, ฟังก์ชั่นเสมือนหรือวิธีเสมือนเป็นฟังก์ชั่นหรือวิธีการที่สืบทอดได้และ overridable หรือวิธีการที่สะดวกในการจัดส่ง แนวคิดนี้เป็นส่วนสำคัญของ (polymorphism) ส่วน (รันไทม์) ของการเขียนโปรแกรมเชิงวัตถุ (OOP) ในระยะสั้นฟังก์ชั่นเสมือนจริงกำหนดฟังก์ชั่นเป้าหมายที่จะดำเนินการ แต่เป้าหมายอาจไม่เป็นที่รู้จักในเวลารวบรวม
แตกต่างจากฟังก์ชั่นที่ไม่ใช่เสมือนเมื่อฟังก์ชั่นเสมือนถูกแทนที่รุ่นที่ได้มามากที่สุดจะถูกใช้ในทุกระดับของลำดับชั้นของชั้นมากกว่าระดับที่มันถูกสร้างขึ้น ดังนั้นหากหนึ่งวิธีของคลาสพื้นฐานเรียกใช้เมธอดเสมือนเวอร์ชันที่กำหนดในคลาสที่ได้รับจะถูกใช้แทนเวอร์ชันที่กำหนดในคลาสพื้นฐาน
นี่คือตรงกันข้ามกับฟังก์ชั่นที่ไม่ใช่เสมือนซึ่งยังคงสามารถถูกแทนที่ในคลาสที่ได้รับ แต่รุ่น "ใหม่" จะถูกใช้โดยคลาสที่ได้รับและด้านล่างเท่านั้น แต่จะไม่เปลี่ยนฟังก์ชั่นของคลาสพื้นฐานเลย
ในขณะที่ ..
ฟังก์ชัน virtual pure หรือ pure virtual method เป็นฟังก์ชันเสมือนที่จำเป็นต้องมีการใช้งานโดยคลาสที่ได้รับหากคลาสที่ได้รับนั้นไม่เป็นนามธรรม
เมื่อมีเมธอดเสมือนจริงคลาสจะเป็น "นามธรรม" และไม่สามารถสร้างอินสแตนซ์ได้ด้วยตัวเอง คลาสที่ได้รับซึ่งใช้วิธี pure-virtual แทนต้องใช้แทน pure-virtual ไม่ได้ถูกกำหนดใน base-class เลยดังนั้นคลาสที่ได้รับจะต้องกำหนดมันหรือคลาสที่ได้รับนั้นเป็นนามธรรมและไม่สามารถสร้างอินสแตนซ์ได้ เฉพาะคลาสที่ไม่มีเมธอด abstract เท่านั้นที่สามารถสร้างอินสแตนซ์
เสมือนมีวิธีการแทนที่การทำงานของชั้นฐานและบริสุทธิ์เสมือนต้องใช้มัน
pure
คำหลัก แต่ Bell Labs กำลังจะเปิดตัว C ++ ครั้งใหญ่และผู้จัดการของเขาจะไม่อนุญาตในช่วงปลายปีนั้น การเพิ่มคำหลักเป็นเรื่องใหญ่
ฉันต้องการแสดงความคิดเห็นเกี่ยวกับคำจำกัดความของวิกิพีเดียเสมือนจริงหลายครั้งที่นี่ [ในขณะที่คำตอบนี้ถูกเขียนขึ้น] 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) คิดว่าวิธีเสมือนเท่านั้นที่สามารถถูกแทนที่ได้
Derived*
ด้วยการเรียกฟังก์ชั่นเดียวกันเพื่อขับรถกลับบ้าน คำตอบที่ยอดเยี่ยมเป็นอย่างอื่น
คำหลักเสมือนจริงให้ความสามารถของ 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) อินเทอร์เฟซ
เหล่านี้เป็นคลาส 'เปล่า' ที่ฟังก์ชันทั้งหมดล้วนเป็นเสมือนจริงและด้วยเหตุนี้คุณต้องสืบทอดและใช้งานฟังก์ชันทั้งหมด
ในคลาส 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
...
};
ด้วยการเพิ่มวิธีเสมือนจริงที่บริสุทธิ์ให้กับคลาสของคุณคุณทำให้คลาสเป็นคลาสพื้นฐานที่เป็นนามธรรม ซึ่งมีประโยชน์มากสำหรับการแยกอินเทอร์เฟซจากการใช้
m_name
แต่สงสัยว่าทำไมคุณเรียกว่าตัวแปรของคุณ อะไรm_
หมายถึง?
"เสมือน" หมายความว่าวิธีนี้อาจถูกแทนที่ในคลาสย่อย แต่มีการใช้งานที่เรียกได้โดยตรงในคลาสฐาน "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()
คำหลักเสมือนทำงานอย่างไร
สมมติว่าชายคนนั้นเป็นคนชั้นฐานอินเดียมาจากคน
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 .... เพราะพวกเขาเขียนภาษา
ฟังก์ชั่นเสมือนจริงเป็นฟังก์ชั่นสมาชิกที่มีการประกาศในคลาสฐานและที่กำหนดใหม่โดยคลาสที่ได้รับ ฟังก์ชันเสมือนเป็นลำดับชั้นตามลำดับการสืบทอด เมื่อคลาสที่ได้รับไม่ได้แทนที่ฟังก์ชันเสมือนฟังก์ชันที่กำหนดภายในคลาสพื้นฐานจะถูกใช้
ฟังก์ชั่นเสมือนที่แท้จริงคือสิ่งที่ไม่มีคำนิยามที่สัมพันธ์กับคลาสพื้นฐาน ไม่มีการนำไปใช้ในคลาสฐาน คลาสที่ได้รับใด ๆ จะต้องแทนที่ฟังก์ชันนี้
Simula, C ++ และ C # ซึ่งใช้การเชื่อมเมธอดสแตติกตามค่าเริ่มต้นโปรแกรมเมอร์สามารถระบุว่าเมธอดเฉพาะควรใช้การเชื่อมแบบไดนามิกโดยการทำเลเบลเป็นเสมือน การเชื่อมเมธอดแบบไดนามิกเป็นศูนย์กลางในการเขียนโปรแกรมเชิงวัตถุ
การเขียนโปรแกรมเชิงวัตถุต้องมีแนวคิดพื้นฐานสามประการ ได้แก่ การห่อหุ้มการสืบทอดและการเชื่อมโยงวิธีการแบบไดนามิก
การห่อหุ้มช่วยให้รายละเอียดการใช้งานของสิ่งที่เป็นนามธรรมถูกซ่อนอยู่หลังส่วนต่อประสานที่เรียบง่าย
การสืบทอดช่วยให้นามธรรมใหม่ถูกกำหนดเป็นส่วนขยายหรือการปรับแต่งของนามธรรมที่มีอยู่บางส่วนได้รับคุณสมบัติบางส่วนหรือทั้งหมดโดยอัตโนมัติ
การเชื่อมเมธอดแบบไดนามิกอนุญาตให้นามธรรมใหม่แสดงพฤติกรรมใหม่แม้ว่าจะใช้ในบริบทที่คาดว่าจะมีนามธรรมเก่า
เมธอดเสมือนสามารถถูกแทนที่ได้โดยการรับคลาส แต่ต้องการการนำไปใช้ในคลาสฐาน (อันที่จะแทนที่)
วิธีเสมือนบริสุทธิ์ไม่มีการใช้คลาสพื้นฐาน พวกเขาจำเป็นต้องกำหนดโดยคลาสที่ได้รับ (ดังนั้นการแทนที่ทางเทคนิคจึงไม่ใช่คำที่ถูกต้องเพราะไม่มีอะไรจะแทนที่)
Virtual สอดคล้องกับพฤติกรรมจาวาเริ่มต้นเมื่อคลาสที่ได้รับมาแทนที่เมธอดของคลาสฐาน
วิธีเพียวเสมือนสอดคล้องกับพฤติกรรมของวิธีนามธรรมภายในคลาสนามธรรม และคลาสที่มีเพียงเมธอดเสมือนจริงและค่าคงที่จะเป็น cpp-pendant ไปยังส่วนต่อประสาน
ฟังก์ชั่นเพียวเสมือน
ลองรหัสนี้
#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 ถูกทำเครื่องหมายเป็นเสมือนในคลาสพื้นฐานมันบอกว่าคอมไพเลอร์ที่พยายามค้นหาฟังก์ชั่นในชั้นเรียนที่ได้รับและใช้ฟังก์ชั่นหากไม่พบแล้วดำเนินการฐานหนึ่งขอบคุณ
"ฟังก์ชั่นเสมือนหรือวิธีเสมือนเป็นฟังก์ชั่นหรือวิธีการที่พฤติกรรมสามารถถูกแทนที่ในชั้นเรียนที่สืบทอดโดยฟังก์ชั่นที่มีลายเซ็นเดียวกัน" - วิกิพีเดีย
นี่ไม่ใช่คำอธิบายที่ดีสำหรับฟังก์ชั่นเสมือน เพราะแม้ว่าสมาชิกจะไม่เสมือนคลาสที่สืบทอดสามารถแทนที่ได้ คุณสามารถลองดูด้วยตัวคุณเอง
ความแตกต่างแสดงให้เห็นตัวเองเมื่อฟังก์ชั่นใช้คลาสฐานเป็นพารามิเตอร์ เมื่อคุณให้คลาสที่สืบทอดเป็นอินพุตฟังก์ชันนั้นใช้การใช้คลาสพื้นฐานของฟังก์ชัน overriden อย่างไรก็ตามถ้าฟังก์ชั่นนั้นเป็นเสมือนจริงมันจะใช้ฟังก์ชันที่นำมาใช้ในคลาสที่ได้รับมา
ฟังก์ชั่นเสมือนจริงจะต้องมีคำจำกัดความในคลาสฐานและในคลาสที่ได้รับมา แต่ไม่จำเป็นตัวอย่างเช่นฟังก์ชัน ToString () หรือ toString () เป็นเสมือนดังนั้นคุณสามารถให้การใช้งานของคุณเองโดยเอาชนะมันในคลาสที่ผู้ใช้กำหนด
ฟังก์ชั่นเสมือนจริงมีการประกาศและกำหนดไว้ในระดับปกติ
ฟังก์ชันเสมือนจริงต้องประกาศที่ลงท้ายด้วย "= 0" และสามารถประกาศได้ในคลาสนามธรรมเท่านั้น
คลาสนามธรรมที่มีฟังก์ชันเสมือนบริสุทธิ์ไม่สามารถมีคำจำกัดความของฟังก์ชันเสมือนบริสุทธิ์นั้นดังนั้นจึงมีความหมายว่าการใช้งานต้องมีให้ในคลาส (es) ที่ได้มาจากคลาสนามธรรมนั้น