การค้นหาวิธีทั่วไปในการเข้าถึง 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 ประเภทต่างๆ ได้อย่างราบรื่น.