พหุรูปแบบแบบคงที่
หลีกเลี่ยงต้นทุนการเรียกเสมือน
พหุรูปแบบแบบคงที่ เป็นบทเรียน C++ Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C++ Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C++ Academy มีบทเรียนทั้งหมด 4 บทเรียน
พหุรูปแบบสองชนิด
C++ รองรับพหุรูปแบบสองลักษณะ
- แบบไดนามิก: ฟังก์ชันเสมือนถูกแก้ไขขณะทำงานผ่านตารางเสมือน
- แบบสแตติก: แม่แบบ/CRTP ถูกแก้ไขขณะคอมไพล์
พหุรูปแบบแบบสแตติกแลกความยืดหยุ่นกับความเร็ว
ต้นทุนของ virtual
การเรียกแบบเสมือนต้องกระโดดทางอ้อมผ่านตารางเสมือน โดยทั่วไปคอมไพเลอร์ไม่สามารถแทรกโค้ดการเรียกนี้ได้ ทำให้การปรับปรุงประสิทธิภาพหลายอย่างถูกขัดขวาง
#include <iostream>
struct Shape {
virtual double area() const = 0;
virtual ~Shape() = default;
};
struct Square : Shape {
double s;
Square(double x) : s(x) {}
double area() const override { return s * s; }
};
int main() {
Shape* p = new Square(3);
std::cout << p->area() << "\n";
delete p;
return 0;
}การเลือกเรียกแบบสแตติกด้วย CRTP
เมื่อใช้ CRTP คลาสพื้นฐานจะเรียกเมธอดของคลาสที่สืบทอดโดยตรงผ่านการแปลงชนิดขณะคอมไพล์ ดังนั้นคอมไพเลอร์จึงสามารถแทรกโค้ดทุกอย่างได้
#include <iostream>
template <typename T>
struct Shape {
double area() const { return static_cast<const T*>(this)->area(); }
};
struct Square : Shape<Square> {
double s;
Square(double x) : s(x) {}
double area() const { return s * s; }
};
int main() {
Square sq(3);
std::cout << sq.area() << "\n";
return 0;
}การตั้งชื่อฟังก์ชันส่งต่อ
เมื่อคลาสพื้นฐานและคลาสที่สืบทอดต่างก็กำหนด area ให้ตั้งชื่อฟังก์ชันส่งต่อของคลาสพื้นฐานให้แตกต่าง เช่น compute เพื่อให้เรียกไปยังการนำไปใช้ของคลาสที่สืบทอดได้อย่างไม่กำกวม
#include <iostream>
template <typename T>
struct Shape {
double compute() const { return static_cast<const T*>(this)->area(); }
};
struct Circle : Shape<Circle> {
double r;
Circle(double x) : r(x) {}
double area() const { return 3.14159 * r * r; }
};
int main() {
Circle c(2);
std::cout << c.compute() << "\n";
return 0;
}แม่แบบในฐานะพหุรูปแบบแบบสแตติก
แม่แบบฟังก์ชันทั่วไปก็เป็นพหุรูปแบบแบบสแตติกเช่นกัน โค้ดเดียวกันทำงานกับชนิดใด ๆ ที่มีส่วนติดต่อที่จำเป็น (การพิมพ์แบบเป็ดขณะคอมไพล์)
#include <iostream>
struct Dog { void speak() const { std::cout << "Woof\n"; } };
struct Cat { void speak() const { std::cout << "Meow\n"; } };
template <typename Animal>
void make_speak(const Animal& a) { a.speak(); }
int main() {
make_speak(Dog{});
make_speak(Cat{});
return 0;
}เมื่อทราบประเภท
พหุสัณฐานแบบคงที่ใช้ได้เฉพาะเมื่อทราบประเภทจริงขณะคอมไพล์ หากต้องเก็บออบเจ็กต์ต่างประเภทไว้ในโครงสร้างเก็บข้อมูลเดียวกันและเลือกพฤติกรรมขณะทำงาน คุณยังคงต้องใช้ฟังก์ชันเสมือน
ไม่มีโครงสร้างเก็บข้อมูลต่างประเภท
คุณไม่สามารถใส่ Shape<Square> และ Shape<Circle> ไว้ใน std::vector เดียวกันโดยใช้เป็นฐานร่วมได้ เพราะทั้งสองเป็นประเภทที่ไม่เกี่ยวข้องกัน นี่คือข้อจำกัดสำคัญของพหุสัณฐานแบบคงที่
ประโยชน์ด้านประสิทธิภาพ
เมื่อแทรกการเรียกที่เลือกไว้ลงในโค้ดโดยตรง คอมไพเลอร์สามารถรวมค่าคงที่และกำจัดค่าใช้จ่ายของการเรียกฟังก์ชันได้ทั้งหมด ในลูปที่ทำงานซ้ำอย่างเข้มข้นกับประเภทเดียวที่ทราบแน่ชัด พหุสัณฐานแบบคงที่อาจทำงานได้เร็วขึ้นอย่างมาก
#include <iostream>
template <typename T>
struct Op {
int apply(int x) const { return static_cast<const T*>(this)->apply(x); }
};
struct Doubler : Op<Doubler> {
int apply(int x) const { return x * 2; }
};
int main() {
Doubler d;
int sum = 0;
for (int i = 0; i < 5; ++i) sum += d.apply(i);
std::cout << sum << "\n";
return 0;
}การใช้ทั้งสองแบบร่วมกัน
คุณสามารถใช้ CRTP สำหรับเส้นทางการทำงานหลัก และใช้ตัวห่อหุ้มฟังก์ชันเสมือนขนาดเล็กเมื่อจำเป็นต้องลบข้อมูลประเภท วิธีนี้ให้ความเร็วในจุดที่สำคัญ และให้ความยืดหยุ่นเมื่อจำเป็น
การตรวจสอบส่วนติดต่อแบบคงที่
หากคลาสสืบทอดลืมใช้งานฟังก์ชันสมาชิกที่จำเป็น ข้อผิดพลาดจะแสดงขึ้นขณะคอมไพล์เมื่อมีการสร้างอินสแตนซ์ของตัวส่งต่อจากฐาน ไม่ใช่ขณะทำงาน
#include <iostream>
template <typename T>
struct Greeter {
void greet() const { static_cast<const T*>(this)->hello(); }
};
struct English : Greeter<English> {
void hello() const { std::cout << "Hello\n"; }
};
int main() {
English{}.greet();
return 0;
}การเลือกเครื่องมือที่เหมาะสม
ใช้พหุสัณฐานแบบคงที่เมื่อ:
- ทราบประเภทขณะคอมไพล์
- ประสิทธิภาพมีความสำคัญอย่างยิ่ง
- ไม่จำเป็นต้องมีประเภทฐานร่วมขณะทำงาน
มิฉะนั้น ควรเลือกใช้ฟังก์ชันเสมือนเพื่อให้โค้ดเข้าใจง่ายและยืดหยุ่น
ตรวจสอบความเข้าใจ
เลือกข้อแลกเปลี่ยนสำคัญของพหุสัณฐานแบบคงที่
สรุปทบทวน
คุณได้เปรียบเทียบพหุสัณฐานแบบคงที่และแบบพลวัต
- ฟังก์ชันเสมือน = การเลือกเรียกขณะทำงาน ตารางฟังก์ชันเสมือน และโครงสร้างเก็บข้อมูลต่างประเภท
- CRTP/แม่แบบ = การเลือกเรียกขณะคอมไพ��� สามารถแทรกโค้ดได้ และทำงานเร็วกว่า
- แบบคงที่ต้องทราบประเภทขณะคอมไพล์
- เลือกใช้โดยพิจารณาความต้องการด้านความยืดหยุ่นเทียบกับประสิทธิภาพ
คำถามที่พบบ่อย
บทเรียน “พหุรูปแบบแบบคงที่” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “พหุรูปแบบแบบคงที่” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C++ Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C++ Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “พหุรูปแบบแบบคงที่”
หลีกเลี่ยงต้นทุนการเรียกเสมือน คุณปฏิบัติ C++ Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C++ Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C++ Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “พหุรูปแบบแบบคงที่” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C++ Academy นี้ได้ไหม
ได้ บทเรียน C++ Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- สำนวน CRTP
- พหุรูปแบบแบบคงที่
- มิกซินด้วย CRTP
- เมื่อใดควรใช้ CRTP