簡體   English   中英

是否可以創建虛擬迭代器 class?

[英]Is it possible to create a virtual iterator class?

我有一個虛擬容器“ISequence”,它用作 arrays 或列表上的容器實現的模板。 我已經為 Array 和 List 容器實現了一個迭代器。 我想做的是創建一個虛擬 IIterator class 以便我可以創建使用迭代器並接受 ISequence 是參數的算法。 我的目標是免於執行。

我嘗試在沒有方法的情況下將虛擬 IIterator class 添加到 ISequence 並且繼承 std::iterator 但這不起作用

IS序列class:

template <typename T>
class ISequence {
protected:
    int length; //length of sequence
public:
    virtual int getLength() const = 0; //get length of sequence
    virtual bool getIsEmpty() const = 0; //check if empty
public:
    virtual T get(int index) const = 0; //get item based on index
    virtual T getFirst() const = 0; //get first item
    virtual T getLast() const = 0; //get last item
    virtual ISequence<T>* getSubSequence(int startIndex, int endIndex) const = 0;
    virtual void append(T item) = 0; //add item to the end
    virtual void prepend(T item) = 0; //add item to the beginning
    virtual void insertAt(int index, T item) = 0; //insert item at a specific point
    virtual void remove(T item) = 0; //remove specific item
    virtual void replace(int index, T item) = 0; //replace an item
};

帶有迭代器的數組:

template <typename T>
class ArraySequence: public ISequence<T> {
private:
    T* data;
public:
    ArraySequence();
    ArraySequence(ISequence<T>* sequence);
    ArraySequence(int n, int leftLimit, int rightLimit);
    ArraySequence<T>& operator=(const ArraySequence<T>& sequence);
    ~ArraySequence();
public:
    virtual int getLength() const override;
    virtual bool getIsEmpty() const override;
public:
    virtual T get(int index) const override;
    virtual T getFirst() const override;
    virtual T getLast() const override;
    virtual ArraySequence<T>* getSubSequence(int startIndex, int endIndex) const override;
    virtual void append(T item) override;
    virtual void prepend(T item) override;
    virtual void insertAt(int index, T item) override;
    virtual void remove(T item) override;
    virtual void replace(int index, T item) override;
private:
    class MyIterator: public std::iterator<std::random_access_iterator_tag, T> {
        friend class ArraySequence;
    private:
        T* pos;
        MyIterator(T* pos);
    public:
        MyIterator(const MyIterator &it);
        ~MyIterator();
    public:
        typename MyIterator::reference operator*() const;
        typename MyIterator::pointer operator->() const;
        typename MyIterator::reference operator[](const typename MyIterator::difference_type& n) const;
        typename MyIterator::difference_type operator-(const MyIterator& it) const;
        MyIterator operator++(int);
        MyIterator& operator++();
        MyIterator operator--(int);
        MyIterator& operator--();
        MyIterator operator+(const typename MyIterator::difference_type& n) const;
        MyIterator& operator+=(const typename MyIterator::difference_type& n);
        MyIterator operator-(const typename MyIterator::difference_type& n) const;
        MyIterator& operator-=(const typename MyIterator::difference_type& n);
        bool operator!=(const MyIterator& it) const;
        bool operator==(const MyIterator& it) const;
        bool operator<(const MyIterator& it) const;
        bool operator>(const MyIterator& it) const;
        bool operator<=(const MyIterator& it) const;
        bool operator>=(const MyIterator& it) const;
    };
public:
    typedef MyIterator iterator;
    typedef MyIterator const const_iterator;
    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;
};

我的抽象 IIterator 代碼(作為 ISequence 的補充):

protected:
    class IIterator: public std::iterator<std::random_access_iterator_tag, T> { //virtual Iterator class
    };
public:
    typedef IIterator iterator;
    typedef IIterator const const_iterator;
    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;

然后我通過 Array 中的 MyIterator 繼承它。

通過我的嘗試,當我運行簡單的 forrange 循環時,我得到“二進制表達式的無效操作數('ISequence::IIterator' 和 'ISequence::IIterator')”錯誤消息。

您正在實施的概念在 C# 中是相當基礎的。 也許有充分的理由在 C++ 中不存在這些? 是的!

您正在嘗試使用 C++ 實現 .Net 庫。 這不是一個好主意,因為該庫的設計迎合了 C# 和 CLR(或者他們現在所說的任何東西)的限制。 C# 在編譯時對類型的處理要少得多,並且它沒有像 C++ 那樣的編譯時泛型類型替換系統。 通過不使用 C++ 提供的功能,您正在重新發明輪子並編寫看起來很不習慣的代碼。 而且它可能會比它需要的慢,因為迭代器上的虛擬方法調用很容易被經常調用,它們的開銷顯示出來。 如果可以的話,編譯器會去虛擬化一些調用,但這不是依賴的東西。

概括一下; 除非另有證明,否則在 C++ 中直接使用來自 .Net 生態系統的大多數慣用語都是默認錯誤的

在 C++ 中,“接口”的概念不依賴於虛擬方法,而是依賴於概念——即對類型的約束。 在 C# 中沒有辦法實現這一點,在一般的 CLR 中也沒有,設計不支持它。 在 C++ 中,只要使用的具體類型符合調用者要求的約束(例如可迭代),編譯器就會處理 rest。 習慣上,您可以使用范圍或迭代器傳遞容器,並且所有接受它們的代碼都應該是通用的:容器或迭代器的類型應該是模板參數,並且迭代器不應該被強制為相同類型- 這確實是舊版本 C++ 庫中的庫設計錯誤。

因此,C++ 的美妙之處在於您無需定義任何接口。 在 C++20 中,您可以使用概念來約束類型,但除此之外,它很簡單 - 比 C# 中的簡單得多。 范圍的想法是讓對象表現得“像”容器,即您可以使用 range-for 對其進行迭代,但它們不一定是容器 - 它們可能只是在容器上表達一系列元素的一種方式,甚至動態生成。

如果您正在考慮這樣做以支持將代碼從 C# 移植到 C++,那么只需忘記對ISequence等的需求。只需使用泛型類型參數,如果您使用 C++20 編譯器,可以選擇使用概念來約束它們,你就設置好了。 就這么容易。 容器可以是任何東西。 即使是普通的“C”數組(顫抖 - 不要使用那些,總是使用std::array代替。)。

假設我們有以下 C# 代碼:

System.IO.TextWriter cout = Console.Out;

Action<System.Collections.IEnumerable> printValues = (values) =>
{
    cout.WriteLine("printValues");
    foreach (var v in values)
        cout.Write($"{v} ");
    cout.Write("\n");
};

var list_of_ints = new List<int>{1, 2, 3, 4, 5};
var vector_of_strings = new String[]{"a", "b", "c", "d"};
cout.WriteLine("* Entire containers");
printValues(list_of_ints);
printValues(vector_of_strings);
cout.WriteLine("\n* Subranges of containers");
printValues(list_of_ints.GetRange(1, list_of_ints.Count() - 1));
printValues(vector_of_strings.Take(vector_of_strings.Count() - 1));

Output:

* Entire containers
printValues
1 2 3 4 5
printValues
a b c d

* Subranges of containers
printValues
2 3 4 5
printValues
a b c

只要您堅持慣用的 C++ 並且不要嘗試在 C++ 中重新實現.Net,它在 C++ 中看起來並沒有太大不同:

#include <forward_list>
#include <iostream>
#include <string>
#include <vector>

// This will accept entire containers, as well as C++20 ranges.
template <class C> void printValues(const C &values) {
    std::cout << __FUNCTION__ << " with container\n";
    for (auto &v : values)
        std::cout << v << " ";
    std::cout << "\n";
}

// This is the more legacy way of doing it - to stay compatible with C++98 (shiver).
template <class I1, class I2> void printValues(I1 start, I2 end) {
    std::cout << __FUNCTION__ << " with iterators\n";
    for (; start != end; ++start)
        std::cout << *start << " ";
    std::cout << "\n";
}

int main() {
    std::forward_list<int> list_of_ints{1,2,3,4,5};
    std::vector<std::string> vector_of_strings{"a", "b", "c", "d"};

    std::cout << "* Entire containers\n";
    printValues(list_of_ints);
    printValues(vector_of_strings);
    std::cout << "\n* Subranges of containers\n";
    printValues(std::next(list_of_ints.cbegin()), list_of_ints.cend());
    printValues(vector_of_strings.cbegin(), std::prev(vector_of_strings.cend()));
}

Output:

* Entire containers
printValues with container
1 2 3 4 5 
printValues with container
a b c d 

* Subranges of containers
printValues with iterators
2 3 4 5 
printValues with iterators
a b c 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM