ฉันได้ยินมาว่าแม่แบบฟังก์ชันคลาสสมาชิก C ++ ไม่สามารถเป็นเสมือนได้ มันเป็นเรื่องจริงเหรอ?
หากพวกเขาสามารถเป็นเสมือนจริงตัวอย่างของสถานการณ์ที่จะใช้ฟังก์ชั่นดังกล่าวคืออะไร?
ฉันได้ยินมาว่าแม่แบบฟังก์ชันคลาสสมาชิก C ++ ไม่สามารถเป็นเสมือนได้ มันเป็นเรื่องจริงเหรอ?
หากพวกเขาสามารถเป็นเสมือนจริงตัวอย่างของสถานการณ์ที่จะใช้ฟังก์ชั่นดังกล่าวคืออะไร?
คำตอบ:
แม่ทุกคนเกี่ยวกับรหัสคอมไพเลอร์สร้างที่รวบรวมเวลา ฟังก์ชั่นเสมือนจริงทั้งหมดเกี่ยวกับระบบเวลาทำการหาฟังก์ชั่นที่จะเรียกใช้ในเวลาทำงาน
เมื่อระบบรันไทม์เข้าใจแล้วมันจะต้องเรียกฟังก์ชันเสมือน templatized การคอมไพล์เสร็จสิ้นและคอมไพเลอร์ไม่สามารถสร้างอินสแตนซ์ที่เหมาะสมได้อีกต่อไป ดังนั้นคุณไม่สามารถมีแม่แบบฟังก์ชันสมาชิกเสมือนได้
แต่มีเทคนิคที่มีประสิทธิภาพและน่าสนใจไม่กี่อันเนื่องมาจากการรวมหลายรูปแบบและแม่แบบสะดุดตาเรียกว่าลบออกประเภท
Virtual functions are all about the run-time system figuring out which function to call at run-time
- ขอโทษ แต่นี่เป็นวิธีที่ผิดไปและค่อนข้างสับสน เป็นเพียงการอ้อมและไม่มี "การหารันไทม์" ที่เกี่ยวข้องมันเป็นที่รู้จักกันในช่วงเวลาที่รวบรวมฟังก์ชั่นที่จะเรียกว่าเป็นสิ่งที่ชี้ไปโดยตัวชี้ n-th ใน vtable "การหา" หมายความว่ามีการตรวจสอบประเภทและไม่ใช่กรณีดังกล่าว Once the run-time system figured out it would need to call a templatized virtual function
- ไม่ว่าจะเป็นฟังก์ชั่นเสมือนหรือไม่เป็นที่รู้จักในเวลารวบรวม
void f(concr_base& cb, virt_base& vb) { cb.f(); vb.f(); }
ดังนั้น "รู้" ว่าฟังก์ชั่นใดถูกเรียกใช้ ณ จุดที่cb.f()
เรียกว่าและไม่ทราบว่าเป็นvb.f()
เช่นนั้น หลังจะต้องมีการพบที่รันไทม์ , โดยระบบรันไทม์ ไม่ว่าคุณจะต้องการเรียกสิ่งนี้ว่า "การหา" และสิ่งนี้มีประสิทธิภาพมากกว่าหรือน้อยกว่าก็ไม่เปลี่ยนข้อเท็จจริงเหล่านี้สักเล็กน้อย
จากเทมเพลต C ++ คู่มือที่สมบูรณ์:
เทมเพลตฟังก์ชันสมาชิกไม่สามารถประกาศเสมือนได้ ข้อ จำกัด นี้ถูกกำหนดเนื่องจากการใช้งานตามปกติของกลไกการเรียกใช้ฟังก์ชันเสมือนใช้ตารางขนาดคงที่ที่มีหนึ่งรายการต่อหนึ่งฟังก์ชันเสมือน อย่างไรก็ตามจำนวนอินสแตนซ์ของแม่แบบฟังก์ชันสมาชิกจะไม่คงที่จนกว่าโปรแกรมทั้งหมดจะถูกแปล ดังนั้นการสนับสนุนเทมเพลตฟังก์ชันสมาชิกเสมือนจะต้องการการสนับสนุนสำหรับกลไกชนิดใหม่ทั้งหมดในคอมไพเลอร์ C ++ และลิงเกอร์ ในทางตรงกันข้ามสมาชิกสามัญของแม่แบบชั้นเรียนสามารถเสมือนจริงได้เนื่องจากจำนวนของพวกเขาได้รับการแก้ไขเมื่อชั้นมีการยกตัวอย่าง
C ++ ไม่อนุญาตฟังก์ชันสมาชิกเทมเพลตเสมือนในขณะนี้ เหตุผลที่เป็นไปได้มากที่สุดคือความซับซ้อนของการนำไปใช้ ราเชนทให้เหตุผลที่ดีว่าทำไมมันไม่สามารถทำได้ในตอนนี้ แต่เป็นไปได้ด้วยการเปลี่ยนแปลงที่สมเหตุสมผลของมาตรฐาน โดยเฉพาะอย่างยิ่งหาจำนวนอินสแตนซ์ของฟังก์ชั่น templated ที่มีอยู่จริงและสร้าง vtable ดูเหมือนยากถ้าคุณพิจารณาสถานที่ของการเรียกฟังก์ชันเสมือน คนมาตรฐานมีหลายอย่างที่ต้องทำในตอนนี้และ C ++ 1x ก็มีงานมากมายสำหรับนักเขียนคอมไพเลอร์เช่นกัน
เมื่อใดที่คุณจะต้องใช้ฟังก์ชันสมาชิก templated ฉันเคยเจอสถานการณ์เช่นนั้นซึ่งฉันพยายามที่จะสร้างโครงสร้างลำดับชั้นใหม่ด้วยคลาสฐานเสมือนจริง มันเป็นสไตล์ที่ไม่ดีสำหรับการใช้กลยุทธ์ที่แตกต่างกัน ฉันต้องการเปลี่ยนอาร์กิวเมนต์ของหนึ่งในฟังก์ชั่นเสมือนเป็นชนิดตัวเลขและแทนที่การโอเวอร์โหลดฟังก์ชันสมาชิกและแทนที่โอเวอร์โหลดทุกอันในคลาสย่อยทั้งหมดที่ฉันพยายามใช้ฟังก์ชั่นเทมเพลตเสมือน (และต้องหาว่ามันไม่มีอยู่จริง) .)
เริ่มจากพื้นหลังบนโต๊ะฟังก์ชั่นเสมือนจริงและวิธีการทำงาน (ที่มา ):
[20.3] ความแตกต่างระหว่างวิธีการเรียกฟังก์ชั่นสมาชิกเสมือนและไม่เสมือนคืออะไร?
ฟังก์ชันที่ไม่ใช่สมาชิกเสมือนจะได้รับการแก้ไขแบบสแตติก นั่นคือฟังก์ชั่นสมาชิกถูกเลือกแบบคงที่ (ณ เวลารวบรวม) ตามประเภทของตัวชี้ (หรือการอ้างอิง) ไปยังวัตถุ
ในทางตรงกันข้ามฟังก์ชั่นสมาชิกเสมือนได้รับการแก้ไขแบบไดนามิก (ในเวลาทำงาน) นั่นคือฟังก์ชั่นสมาชิกถูกเลือกแบบไดนามิก (ในเวลาทำงาน) ตามประเภทของวัตถุไม่ใช่ประเภทของตัวชี้ / การอ้างอิงไปยังวัตถุนั้น สิ่งนี้เรียกว่า "การรวมแบบไดนามิก" คอมไพเลอร์ส่วนใหญ่ใช้ตัวแปรบางส่วนของเทคนิคต่อไปนี้: หากวัตถุมีฟังก์ชันเสมือนหนึ่งฟังก์ชันขึ้นไปคอมไพเลอร์จะวางตัวชี้ที่ซ่อนอยู่ในวัตถุที่เรียกว่า "ตัวชี้เสมือนจริง" หรือ "ตัวชี้ v" v-pointer นี้ชี้ไปที่ตารางทั่วโลกที่เรียกว่า "virtual-table" หรือ "v-table"
คอมไพเลอร์สร้าง v-table สำหรับแต่ละคลาสที่มีฟังก์ชันเสมือนอย่างน้อยหนึ่งฟังก์ชัน ตัวอย่างเช่นหาก class Circle มีฟังก์ชันเสมือนสำหรับ draw () และ move () และ resize () จะมีหนึ่ง v-table ที่เกี่ยวข้องกับ class Circle แม้ว่าจะมีวัตถุ Circle gazillion และตัวชี้ v ของ แต่ละวัตถุ Circle เหล่านั้นจะชี้ไปที่ Circle v-table ตาราง v นั้นมีพอยน์เตอร์สำหรับแต่ละฟังก์ชันเสมือนในคลาส ตัวอย่างเช่น Circle v-table จะมีสามพอยน์เตอร์: ตัวชี้ไปที่ Circle :: draw (), ตัวชี้ไปที่ Circle :: move () และตัวชี้ไปที่ Circle :: resize ()
ในระหว่างการแจกจ่ายฟังก์ชันเสมือนระบบรันไทม์จะติดตามตัวชี้ v ของวัตถุไปยังตาราง v ของคลาสจากนั้นติดตามช่องที่เหมาะสมใน v-table ไปยังรหัสวิธีการ
ค่าใช้จ่ายด้านพื้นที่ของเทคนิคด้านบนนั้นเป็นชื่อ: ตัวชี้พิเศษต่อวัตถุ (แต่สำหรับวัตถุที่จะต้องทำการเชื่อมโยงแบบไดนามิก) รวมถึงตัวชี้พิเศษต่อวิธี (แต่สำหรับวิธีเสมือนเท่านั้น) ค่าใช้จ่ายเวลายังค่อนข้างน้อย: เมื่อเทียบกับการเรียกใช้ฟังก์ชันปกติการเรียกใช้ฟังก์ชันเสมือนต้องใช้การดึงข้อมูลเพิ่มเติมสองครั้ง (หนึ่งเพื่อรับค่าของตัวชี้ v- หนึ่งวินาทีเพื่อรับที่อยู่ของวิธีการ) ไม่มีกิจกรรมรันไทม์นี้เกิดขึ้นกับฟังก์ชั่นที่ไม่ใช่เสมือนเนื่องจากคอมไพเลอร์แก้ไขฟังก์ชันที่ไม่ใช่เสมือนเฉพาะในเวลาคอมไพล์ตามประเภทของตัวชี้
ฉันพยายามที่จะใช้สิ่งนี้ในขณะนี้สำหรับคลาสฐาน cubefile ที่มีฟังก์ชั่นโหลดเพิ่มประสิทธิภาพ templated ซึ่งจะนำไปใช้งานที่แตกต่างกันสำหรับประเภทของคิวบ์ที่แตกต่างกัน
บางรหัส:
virtual void LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
สิ่งที่ฉันต้องการให้เป็น แต่จะไม่รวบรวมเนื่องจากคอมโบเสมือน templated:
template<class T>
virtual void LoadCube(UtpBipCube<T> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
ฉันสิ้นสุดการย้ายประกาศแม่แบบให้อยู่ในระดับชั้นเรียน โซลูชันนี้จะบังคับให้โปรแกรมรู้เกี่ยวกับประเภทข้อมูลเฉพาะที่พวกเขาจะอ่านก่อนที่จะอ่านซึ่งไม่สามารถยอมรับได้
คำเตือนนี่ไม่สวยมาก แต่มันอนุญาตให้ฉันลบรหัสการดำเนินการซ้ำ ๆ
1) ในคลาสฐาน
virtual void LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
2) และในชั้นเรียนของเด็ก
void LoadCube(UtpBipCube<float> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }
void LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }
void LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }
template<class T>
void LoadAnyCube(UtpBipCube<T> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1);
โปรดทราบว่า LoadAnyCube จะไม่ถูกประกาศในคลาสฐาน
นี่เป็นอีกหนึ่งคำตอบที่กองล้นกับการทำงานรอบ: ต้องมีวิธีแก้ปัญหาแม่แบบเสมือนสมาชิก
รหัสต่อไปนี้สามารถรวบรวมและทำงานอย่างถูกต้องโดยใช้ MinGW G ++ 3.4.5 ในหน้าต่าง 7:
#include <iostream>
#include <string>
using namespace std;
template <typename T>
class A{
public:
virtual void func1(const T& p)
{
cout<<"A:"<<p<<endl;
}
};
template <typename T>
class B
: public A<T>
{
public:
virtual void func1(const T& p)
{
cout<<"A<--B:"<<p<<endl;
}
};
int main(int argc, char** argv)
{
A<string> a;
B<int> b;
B<string> c;
A<string>* p = &a;
p->func1("A<string> a");
p = dynamic_cast<A<string>*>(&c);
p->func1("B<string> c");
B<int>* q = &b;
q->func1(3);
}
และผลลัพธ์คือ:
A:A<string> a
A<--B:B<string> c
A<--B:3
และต่อมาฉันได้เพิ่ม class X ใหม่:
class X
{
public:
template <typename T>
virtual void func2(const T& p)
{
cout<<"C:"<<p<<endl;
}
};
เมื่อฉันพยายามใช้ class X ใน main () ดังนี้:
X x;
x.func2<string>("X x");
g ++ รายงานข้อผิดพลาดต่อไปนี้:
vtempl.cpp:34: error: invalid use of `virtual' in template declaration of `virtu
al void X::func2(const T&)'
ดังนั้นจึงเป็นที่ชัดเจนว่า:
ไม่พวกเขาไม่สามารถ แต่:
template<typename T>
class Foo {
public:
template<typename P>
void f(const P& p) {
((T*)this)->f<P>(p);
}
};
class Bar : public Foo<Bar> {
public:
template<typename P>
void f(const P& p) {
std::cout << p << std::endl;
}
};
int main() {
Bar bar;
Bar *pbar = &bar;
pbar -> f(1);
Foo<Bar> *pfoo = &bar;
pfoo -> f(1);
};
มีผลเหมือนกันมากถ้าทุกสิ่งที่คุณต้องการคือมีอินเทอร์เฟซทั่วไปและเลื่อนการใช้งานไปที่คลาสย่อย
Foo
ชี้มีคุณสมบัติตามที่Foo<Bar>
มันไม่สามารถชี้ไปที่หรือFoo<Barf>
Foo<XXX>
ไม่ฟังก์ชันสมาชิกเทมเพลตไม่สามารถเป็นเสมือนได้
ในคำตอบอื่น ๆ ฟังก์ชั่นแม่แบบที่นำเสนอเป็นส่วนหน้าและไม่มีประโยชน์ใด ๆ ในทางปฏิบัติ
ภาษาไม่อนุญาตให้มีฟังก์ชั่นเทมเพลตเสมือน แต่มีวิธีแก้ไขที่เป็นไปได้ที่จะมีทั้งสองอย่างเช่นการใช้เทมเพลตเดียวสำหรับแต่ละคลาสและอินเทอร์เฟซเสมือนทั่วไป
อย่างไรก็ตามมีความจำเป็นต้องกำหนดสำหรับแต่ละประเภทเทมเพลตรวมถึงฟังก์ชั่น wrapper เสมือนจริง:
#include <memory>
#include <iostream>
#include <iomanip>
//---------------------------------------------
// Abstract class with virtual functions
class Geometry {
public:
virtual void getArea(float &area) = 0;
virtual void getArea(long double &area) = 0;
};
//---------------------------------------------
// Square
class Square : public Geometry {
public:
float size {1};
// virtual wrapper functions call template function for square
virtual void getArea(float &area) { getAreaT(area); }
virtual void getArea(long double &area) { getAreaT(area); }
private:
// Template function for squares
template <typename T>
void getAreaT(T &area) {
area = static_cast<T>(size * size);
}
};
//---------------------------------------------
// Circle
class Circle : public Geometry {
public:
float radius {1};
// virtual wrapper functions call template function for circle
virtual void getArea(float &area) { getAreaT(area); }
virtual void getArea(long double &area) { getAreaT(area); }
private:
// Template function for Circles
template <typename T>
void getAreaT(T &area) {
area = static_cast<T>(radius * radius * 3.1415926535897932385L);
}
};
//---------------------------------------------
// Main
int main()
{
// get area of square using template based function T=float
std::unique_ptr<Geometry> geometry = std::make_unique<Square>();
float areaSquare;
geometry->getArea(areaSquare);
// get area of circle using template based function T=long double
geometry = std::make_unique<Circle>();
long double areaCircle;
geometry->getArea(areaCircle);
std::cout << std::setprecision(20) << "Square area is " << areaSquare << ", Circle area is " << areaCircle << std::endl;
return 0;
}
เอาท์พุท:
พื้นที่สแควร์คือ 1, พื้นที่วงกลมคือ 3.1415926535897932385
ลองที่นี่
วิธีตอบคำถามส่วนที่สอง:
หากพวกเขาสามารถเป็นเสมือนจริงตัวอย่างของสถานการณ์ที่จะใช้ฟังก์ชั่นดังกล่าวคืออะไร?
นี่ไม่ใช่สิ่งที่ไร้เหตุผลที่ต้องการทำ ตัวอย่างเช่น Java (ซึ่งทุกวิธีเป็นเสมือน) ไม่มีปัญหากับวิธีการทั่วไป
ตัวอย่างหนึ่งในภาษาซีพลัสพลัสที่ต้องการเทมเพลตฟังก์ชันเสมือนคือฟังก์ชันสมาชิกที่ยอมรับตัววนซ้ำทั่วไป หรือฟังก์ชันสมาชิกที่ยอมรับวัตถุฟังก์ชันทั่วไป
ทางออกของปัญหานี้คือการใช้การลบประเภทด้วยฟังก์ชั่น boost :: any_range และ boost :: ซึ่งจะช่วยให้คุณสามารถยอมรับตัววนซ้ำทั่วไปหรือ functor โดยไม่จำเป็นต้องทำหน้าที่เป็นเทมเพลต
มีวิธีแก้ไขสำหรับ 'วิธีแม่แบบเสมือนจริง' หากทราบประเภทชุดสำหรับวิธีแม่แบบล่วงหน้า
ในการแสดงแนวคิดในตัวอย่างด้านล่างมีการใช้งานเพียงสองประเภทเท่านั้น ( int
และdouble
)
มีวิธีแม่แบบ 'เสมือน' ( Base::Method
) เรียกวิธีเสมือนที่สอดคล้องกัน (หนึ่งในBase::VMethod
) ซึ่งในทางกลับกันเรียกการใช้วิธีการแม่แบบ (Impl::TMethod
)
เดียวต้องใช้วิธีการแม่แบบTMethod
ในการใช้งานมา ( AImpl
, BImpl
) Derived<*Impl>
และการใช้งาน
class Base
{
public:
virtual ~Base()
{
}
template <typename T>
T Method(T t)
{
return VMethod(t);
}
private:
virtual int VMethod(int t) = 0;
virtual double VMethod(double t) = 0;
};
template <class Impl>
class Derived : public Impl
{
public:
template <class... TArgs>
Derived(TArgs&&... args)
: Impl(std::forward<TArgs>(args)...)
{
}
private:
int VMethod(int t) final
{
return Impl::TMethod(t);
}
double VMethod(double t) final
{
return Impl::TMethod(t);
}
};
class AImpl : public Base
{
protected:
AImpl(int p)
: i(p)
{
}
template <typename T>
T TMethod(T t)
{
return t - i;
}
private:
int i;
};
using A = Derived<AImpl>;
class BImpl : public Base
{
protected:
BImpl(int p)
: i(p)
{
}
template <typename T>
T TMethod(T t)
{
return t + i;
}
private:
int i;
};
using B = Derived<BImpl>;
int main(int argc, const char* argv[])
{
A a(1);
B b(1);
Base* base = nullptr;
base = &a;
std::cout << base->Method(1) << std::endl;
std::cout << base->Method(2.0) << std::endl;
base = &b;
std::cout << base->Method(1) << std::endl;
std::cout << base->Method(2.0) << std::endl;
}
เอาท์พุท:
0
1
2
3
หมายเหตุ:
Base::Method
เกินจริงสำหรับรหัสจริง ( VMethod
สามารถทำให้เป็นสาธารณะและใช้โดยตรง) ฉันเพิ่มมันเพื่อให้ดูเหมือนเป็นวิธีแม่แบบ 'เสมือนจริง'
Base
ระดับเดิมทุกครั้งที่คุณต้องเรียกฟังก์ชั่นแม่แบบที่มีประเภทอาร์กิวเมนต์ไม่เข้ากันได้กับคนที่ดำเนินการจนถึงปัจจุบัน การหลีกเลี่ยงความจำเป็นนี้เป็นความตั้งใจของเทมเพลต ...
ในขณะที่คำถามรุ่นเก่าที่ได้รับคำตอบจากหลายคนฉันเชื่อว่าวิธีรวบรัดไม่แตกต่างจากคนอื่น ๆ ที่โพสต์คือการใช้แมโครรองเพื่อช่วยลดความซ้ำซ้อนของการประกาศในชั้นเรียน
// abstract.h
// Simply define the types that each concrete class will use
#define IMPL_RENDER() \
void render(int a, char *b) override { render_internal<char>(a, b); } \
void render(int a, short *b) override { render_internal<short>(a, b); } \
// ...
class Renderable
{
public:
// Then, once for each on the abstract
virtual void render(int a, char *a) = 0;
virtual void render(int a, short *b) = 0;
// ...
};
ดังนั้นตอนนี้เพื่อใช้คลาสย่อยของเรา:
class Box : public Renderable
{
public:
IMPL_RENDER() // Builds the functions we want
private:
template<typename T>
void render_internal(int a, T *b); // One spot for our logic
};
ประโยชน์ที่นี่คือเมื่อเพิ่มประเภทที่รองรับใหม่สามารถทำได้จากส่วนหัวนามธรรมและอาจนำไปแก้ไขในไฟล์ต้นฉบับ / ส่วนหัวหลายไฟล์
อย่างน้อยที่สุดด้วยฟังก์ชันเสมือน gcc 5.4 อาจเป็นสมาชิกเทมเพลต แต่ต้องเป็นเทมเพลตด้วยตนเอง
#include <iostream>
#include <string>
class first {
protected:
virtual std::string a1() { return "a1"; }
virtual std::string mixt() { return a1(); }
};
class last {
protected:
virtual std::string a2() { return "a2"; }
};
template<class T> class mix: first , T {
public:
virtual std::string mixt() override;
};
template<class T> std::string mix<T>::mixt() {
return a1()+" before "+T::a2();
}
class mix2: public mix<last> {
virtual std::string a1() override { return "mix"; }
};
int main() {
std::cout << mix2().mixt();
return 0;
}
เอาท์พุท
mix before a2
Process finished with exit code 0
ลองสิ่งนี้:
เขียนใน classeder.h:
template <typename T>
class Example{
public:
T c_value;
Example(){}
T Set(T variable)
{
return variable;
}
virtual Example VirtualFunc(Example paraM)
{
return paraM.Set(c_value);
}
ตรวจสอบหากทำงานกับสิ่งนี้เพื่อเขียนโค้ดนี้ใน main.cpp:
#include <iostream>
#include <classeder.h>
int main()
{
Example exmpl;
exmpl.c_value = "Hello, world!";
std::cout << exmpl.VirtualFunc(exmpl);
return 0;
}