การค้นหาวิธีทั่วไปในการเข้าถึง Container ที่หลากหลายใน C++
ใน C++ ที่ทันสมัย ปัญหาทั่วไปที่นักพัฒนาต้องเผชิญ คือ การวนลูปผ่าน Container ประเภทต่างๆ อย่างสม่ำเสมอ ลองนึกภาพว่าคุณมี Container หลายๆ ตัว เช่น std::vector
, std::list
, และรายการแบบกำหนดเอง แต่ละ Container มีประเภทของตัววนลูปที่เป็นเอกลักษณ์ ซึ่งอาจทำให้การเขียนโค้ดที่ชัดเจนและดูแลรักษาง่ายยาก ในโพสต์บล็อกนี้ เราจะสำรวจปัญหานี้และนำเสนอวิธีการที่สง่างามในการเข้าถึง Container เหล่านี้ในแบบทั่วไป
ปัญหา
เมื่อคุณพยายามเข้าถึงรายการใน Container ที่หลากหลาย คุณจะเผชิญกับปัญหาหนึ่ง โดยในคลาส Foo
คุณต้องการให้มีตัววนรอบเดียวที่สามารถโต้ตอบกับ std::vector<int>
, std::list<int>
และรายการแบบกำหนดเองของคุณ อย่างไรก็ตาม เนื่องจาก Container ทั้งหมดนี้คืนค่าประเภทตัววนลูปที่แตกต่างกัน ความพยายามของคุณในการกำหนดค่าตัววนลูปหนึ่งจะนำไปสู่ความขัดแย้งในประเภท
นี่คือเวอร์ชันง่าย ๆ ของความท้าทายที่คุณเผชิญ:
class Foo {
public:
Foo() {
std::list<int> x;
std::vector<int> y;
custom_list<int> z;
iter = x.begin(); // หรือ
iter = y.begin(); // หรือ
iter = z.begin(); // ปัญหา: ประเภทตัววนลูปที่แตกต่างกัน
};
private:
std::iterator<int> iter; // แบบนี้ใช้ไม่ได้
};
วิธีแก้ปัญหา
ในการแก้ไขปัญหานี้ในขณะที่ยังคงให้โค้ดของคุณสวยงามและจัดการได้ เราสามารถใช้แนวคิดที่เรียกว่า การลบประเภท เทคนิคนี้ช่วยให้เราสามารถสร้างอินเทอร์เฟซทั่วไปสำหรับตัววนลูป เพื่อให้มีวิธีการเข้าถึงรายการได้อย่างสม่ำเสมอใน Container ประเภทต่างๆ
ขั้นตอนที่ 1: คลาสตัววนรอบพื้นฐาน
เราจะเริ่มต้นด้วยการกำหนดอินเทอร์เฟซพื้นฐานที่ตัววนรอบทั้งหมดจะสืบทอดจาก ซึ่งจะให้วิธีการทั่วไปในการโต้ตอบกับประเภทตัววนรอบที่แตกต่างกัน
class IIterator {
public:
virtual ~IIterator() = default;
virtual int& operator*() = 0; // ตัวดำเนินการdereference
virtual IIterator& operator++() = 0; // ตัวดำเนินการเพิ่ม
virtual bool operator!=(const IIterator& other) const = 0; // ตัวดำเนินการเปรียบเทียบ
};
ขั้นตอนที่ 2: การใช้งานสำหรับแต่ละประเภท Container
ถัดไป เราจะนำอินเทอร์เฟซนี้ไปใช้งานสำหรับประเภท Container ที่แตกต่างกัน
สำหรับ std::vector
class VectorIterator : public IIterator {
std::vector<int>::iterator it;
public:
VectorIterator(std::vector<int>::iterator iterator) : it(iterator) {}
int& operator*() override { return *it; }
IIterator& operator++() override { ++it; return *this; }
bool operator!=(const IIterator& other) const override {
return this->it != dynamic_cast<const VectorIterator&>(other).it;
}
};
สำหรับ std::list
class ListIterator : public IIterator {
std::list<int>::iterator it;
public:
ListIterator(std::list<int>::iterator iterator) : it(iterator) {}
int& operator*() override { return *it; }
IIterator& operator++() override { ++it; return *this; }
bool operator!=(const IIterator& other) const override {
return this->it != dynamic_cast<const ListIterator&>(other).it;
}
};
สำหรับ Custom List
สมมติว่ารายการแบบกำหนดเองของคุณทำการนิยามตัววนรอบให้มีอยู่ คุณสามารถนำมันไปใช้งานในลักษณะเดียวกันดังนี้:
class CustomListIterator : public IIterator {
custom_list<int>::iterator it;
public:
CustomListIterator(custom_list<int>::iterator iterator) : it(iterator) {}
int& operator*() override { return *it; }
IIterator& operator++() override { ++it; return *this; }
bool operator!=(const IIterator& other) const override {
return this->it != dynamic_cast<const CustomListIterator&>(other).it;
}
};
ขั้นตอนที่ 3: การใช้ตัววนรอบใน Foo
ตอนนี้ ในคลาส Foo
ของคุณ คุณสามารถเก็บตัวชี้ประเภทเดียวไปยังคลาสพื้นฐาน IIterator
เพื่อจัดการกับประเภทของ Container ใด ๆ
class Foo {
public:
Foo() {
std::list<int> x;
std::vector<int> y;
custom_list<int> z;
IIterator* iter;
// สมมติว่าคุณมีอินสแตนซ์ของแต่ละ Container
iter = new VectorIterator(y.begin());
// หรือ
iter = new ListIterator(x.begin());
// หรือ
iter = new CustomListIterator(z.begin());
// ตอนนี้คุณสามารถใช้ iter เป็นตัววนรอบที่สม่ำเสมอได้
};
};
สรุป
โดยการใช้ การลบประเภท ผ่านอินเทอร์เฟซตัววนรอบทั่วไป เราได้จัดเตรียมวิธีการที่สง่างามในการจัดการกับประเภท Container ที่แตกต่างกันใน C++ ซึ่งช่วยให้งานเขียนโค้ดสะอาดและดูแลรักษาง่ายขึ้น โดยหลีกเลี่ยงความยุ่งเหยิงจากการจัดการประเภทตัววนลูปหลายประเภท
สำหรับการสำรวจหัวข้อนี้เพิ่มเติม คุณอาจพิจารณาอ่านบทความที่มีข้อมูลเชิงลึกเหล่านี้:
โซลูชันนี้ไม่เพียงแต่ช่วยเพิ่มทักษะ C++ ของคุณ แต่ยังช่วยสร้างโครงสร้างโค้ดที่สามารถนำกลับมาใช้ใหม่และสามารถจัดการ Container ประเภทต่างๆ ได้อย่างราบรื่น.