[英]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.