Mencari Cara Generik untuk Mengakses Kontainer Beragam di C++

Dalam C++ modern, satu tantangan umum yang dihadapi pengembang adalah mengiterasi berbagai jenis kontainer secara seragam. Bayangkan Anda memiliki beberapa kontainer: std::vector standar, std::list, dan sebuah daftar kustom. Setiap kontainer memiliki jenis iterator yang unik, yang dapat menyulitkan penulisan kode yang bersih dan dapat dipelihara. Dalam pos blog ini, kita akan mengeksplorasi masalah ini dan menyajikan solusi elegan untuk mengakses kontainer-kontainer ini secara generik.

Masalah

Saat Anda mencoba mengakses item dalam kontainer yang beragam ini, Anda dengan cepat menghadapi masalah. Dalam kelas Foo Anda, Anda menginginkan satu iterator yang dapat berinteraksi dengan std::vector<int>, std::list<int>, dan daftar kustom Anda. Namun, karena semua kontainer ini mengembalikan jenis iterator yang berbeda, upaya Anda untuk menetapkan satu iterator akan menyebabkan konflik jenis.

Berikut adalah versi sederhana dari tantangan yang Anda hadapi:

class Foo {
public:
    Foo() {
        std::list<int> x;
        std::vector<int> y;
        custom_list<int> z;

        iter = x.begin(); // ATAU
        iter = y.begin(); // ATAU
        iter = z.begin(); // Masalah: Jenis iterator yang berbeda
    };
private:
    std::iterator<int> iter; // Ini tidak akan berfungsi
};

Solusi

Untuk menyelesaikan masalah ini sambil menjaga kode Anda elegan dan dapat dikelola, kita dapat memanfaatkan konsep yang dikenal sebagai penghapusan jenis. Teknik ini memungkinkan kita untuk membuat antarmuka generik untuk iterator, memberikan cara yang seragam untuk mengakses item di berbagai jenis kontainer.

Langkah 1: Kelas Dasar Iterator

Kita akan mulai dengan mendefinisikan antarmuka dasar yang akan diwarisi oleh semua iterator. Ini memberikan pendekatan yang umum untuk berinteraksi dengan berbagai tipe iterator.

class IIterator {
public:
    virtual ~IIterator() = default;
    virtual int& operator*() = 0; // Operator dereferensi
    virtual IIterator& operator++() = 0; // Operator increment
    virtual bool operator!=(const IIterator& other) const = 0; // Operator perbandingan
};

Langkah 2: Implementasi untuk Setiap Jenis Kontainer

Selanjutnya, kita akan mengimplementasikan antarmuka ini untuk berbagai jenis kontainer.

Untuk 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;
    }
};

Untuk 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;
    }
};

Untuk Daftar Kustom

Dengan asumsi daftar kustom Anda yang mendefinisikan iterator tersedia, Anda akan menerapkannya serupa seperti di bawah ini:

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;
    }
};

Langkah 3: Menggunakan Iterator dalam Foo

Sekarang, di kelas Foo Anda, Anda dapat mempertahankan satu jenis pointer ke kelas dasar IIterator untuk menangani jenis kontainer apa pun.

class Foo {
public:
    Foo() {
        std::list<int> x;
        std::vector<int> y;
        custom_list<int> z;

        IIterator* iter;

        // Mari kita anggap Anda memiliki instance dari setiap kontainer
        iter = new VectorIterator(y.begin());
        // ATAU
        iter = new ListIterator(x.begin());
        // ATAU
        iter = new CustomListIterator(z.begin());

        // Anda sekarang dapat menggunakan iter sebagai iterator yang seragam
    };
};

Kesimpulan

Dengan memanfaatkan penghapusan jenis melalui antarmuka iterator yang umum, kita telah menyediakan cara elegan untuk memanipulasi berbagai jenis kontainer di C++. Ini memungkinkan kode yang lebih bersih dan lebih mudah dipelihara sambil menghindari masalah dalam mengelola beberapa jenis iterator.

Untuk eksplorasi lebih lanjut tentang topik ini, pertimbangkan untuk membaca beberapa artikel yang menarik:

Solusi ini tidak hanya meningkatkan keterampilan C++ Anda tetapi juga membantu menciptakan struktur kode yang serbaguna dan dapat digunakan kembali yang dapat menangani berbagai jenis kontainer dengan mulus.