C++中如何重写包含STL指针容器的类的迭代器?
你遇到的问题很典型——想要对外暴露一个迭代器,但不想泄露内部的指针细节(这里是B*),而是直接返回引用B&。手动重写所有迭代器方法确实繁琐,下面有几种更优雅的实现方式:
方案1:C++20 标准库的 std::iterator_adaptor
C++20在<iterator>中引入了std::iterator_adaptor,它可以帮你快速包装现有迭代器,只需要重写你需要修改的操作(比如operator*),其他操作(递增、递减、比较等)会自动委托给底层迭代器。
代码示例:
#include <vector> #include <iterator> // 包含std::iterator_adaptor class B {}; class A { private: std::vector<B*> data_; // 定义迭代器适配器 class iterator : public std::iterator_adaptor< iterator, // 自身类型 std::vector<B*>::iterator, // 底层迭代器类型 B, // 迭代器的值类型(我们想要暴露的类型) std::forward_iterator_tag, // 迭代器类别,根据你的需求调整 B& // operator*的返回类型 > { public: // 构造函数,委托给底层迭代器 explicit iterator(std::vector<B*>::iterator it) : iterator_adaptor_base(it) {} // 重写operator*,返回引用而不是指针 B& operator*() const { return *this->base(); // base()返回底层迭代器,解引用得到B*,再解引用得到B& } // 如果需要支持->,可以重写它 B* operator->() const { return this->base().operator->(); // 或者直接return *this->base(); } }; public: // 对外暴露迭代器的begin/end iterator begin() { return iterator(data_.begin()); } iterator end() { return iterator(data_.end()); } // 同理可以定义const_iterator,只需要修改值类型和返回类型为const B& class const_iterator : public std::iterator_adaptor< const_iterator, std::vector<B*>::const_iterator, const B, std::forward_iterator_tag, const B& > { public: explicit const_iterator(std::vector<B*>::const_iterator it) : iterator_adaptor_base(it) {} const B& operator*() const { return *this->base(); } const B* operator->() const { return this->base().operator->(); } }; const_iterator begin() const { return const_iterator(data_.begin()); } const_iterator end() const { return const_iterator(data_.end()); } };
这个方案的优点是不用手动实现所有迭代器的核心方法(operator++、operator==等),std::iterator_adaptor已经帮你处理好了,你只需要关注需要定制的operator*和operator->。
方案2:手动实现轻量适配器(兼容C++11及以上)
如果你的项目还不能用C++20,可以自己写一个简单的适配器,利用继承底层迭代器来复用大部分操作,只重写operator*:
#include <vector> class B {}; class A { private: std::vector<B*> data_; class iterator : public std::vector<B*>::iterator { public: // 继承底层迭代器的构造函数 using std::vector<B*>::iterator::iterator; // 重写operator* B& operator*() const { // 调用父类的operator*得到B*,再解引用为B& return *std::vector<B*>::iterator::operator*(); } // 重写operator->(如果需要) B* operator->() const { return std::vector<B*>::iterator::operator*(); } }; public: iterator begin() { return iterator(data_.begin()); } iterator end() { return iterator(data_.end()); } // const版本同理 class const_iterator : public std::vector<B*>::const_iterator { public: using std::vector<B*>::const_iterator::const_iterator; const B& operator*() const { return *std::vector<B*>::const_iterator::operator*(); } const B* operator->() const { return std::vector<B*>::const_iterator::operator*(); } }; const_iterator begin() const { return const_iterator(data_.begin()); } const_iterator end() const { return const_iterator(data_.end()); } };
这个方案的好处是兼容性好,不需要依赖C++20特性。通过继承std::vector<B*>::iterator,我们自动获得了所有迭代器的操作(递增、递减、比较等),只需要重写operator*和operator->来改变返回类型。
如果想要更标准、不依赖STL迭代器继承性的写法,可以把底层迭代器作为成员变量,复用类型定义并委托核心操作:
#include <vector> #include <iterator> class B {}; class A { private: std::vector<B*> data_; class iterator { public: // 复用底层迭代器的类型定义 using value_type = B; using reference = B&; using pointer = B*; using difference_type = std::vector<B*>::iterator::difference_type; using iterator_category = std::vector<B*>::iterator::iterator_category; // 构造函数 explicit iterator(std::vector<B*>::iterator it) : it_(it) {} // 委托递增操作 iterator& operator++() { ++it_; return *this; } iterator operator++(int) { iterator temp = *this; ++it_; return temp; } // 委托比较操作 bool operator==(const iterator& other) const { return it_ == other.it_; } bool operator!=(const iterator& other) const { return !(*this == other); } // 重写解引用 B& operator*() const { return *(*it_); } B* operator->() const { return *it_; } private: std::vector<B*>::iterator it_; }; public: iterator begin() { return iterator(data_.begin()); } iterator end() { return iterator(data_.end()); } };
这种方式更符合标准规范,仅需要手动实现少量核心操作,比完全重写所有方法高效很多。
方案3:使用Boost库的boost::iterator_adaptor
如果你的项目允许使用Boost库,boost::iterator_adaptor是C20之前的成熟解决方案,用法和C20的std::iterator_adaptor类似,能帮你省去大量重复代码:
#include <vector> #include <boost/iterator/iterator_adaptor.hpp> class B {}; class A { private: std::vector<B*> data_; class iterator : public boost::iterator_adaptor< iterator, std::vector<B*>::iterator, B, boost::use_default, B& > { public: explicit iterator(std::vector<B*>::iterator it) : iterator_adaptor(it) {} private: // 让boost::iterator_adaptor可以访问底层实现 friend class boost::iterator_core_access; // 重写dereference操作 B& dereference() const { return *(*this->base()); } }; public: iterator begin() { return iterator(data_.begin()); } iterator end() { return iterator(data_.end()); } };
Boost的适配器同样帮你处理了所有迭代器的核心操作,你只需要实现dereference()方法即可。
总结来说,最优雅的方案是C20的std::iterator_adaptor,如果无法使用C20,手动继承底层迭代器或轻量适配器是不错的选择,而Boost库则是成熟的第三方解决方案。这些方法都能避免手动重写所有迭代器成员函数,同时完美隐藏内部的指针实现。
内容的提问来源于stack exchange,提问作者manolo

