You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为IDocument接口设计begin()/end()虚函数的返回类型?

为抽象文档接口实现迭代器访问的解决方案

针对在不暴露派生类容器类型的前提下,给抽象基类IDocument添加begin()/end()虚函数以支持遍历的需求,核心思路是类型擦除:通过自定义抽象迭代器接口,封装不同容器的迭代器实现,同时保持接口的一致性。

步骤1:定义抽象迭代器基类

首先创建一个抽象迭代器类,统一迭代器的核心操作,避免依赖具体容器的迭代器类型:

#include <memory>
#include <deque>
#include <optional>
#include <string>
#include <iterator>

// 假设DocumentItem、StdPath、UndoManager是已定义的类型
class DocumentItem;
class StdPath;
class UndoManager;

class IDocumentIterator {
public:
    using value_type = DocumentItem;
    using reference = const DocumentItem&;
    using pointer = const DocumentItem*;

    virtual ~IDocumentIterator() = default;
    // 用于拷贝迭代器(支持值语义)
    virtual IDocumentIterator* clone() const = 0;

    // 迭代器核心操作
    virtual reference operator*() const = 0;
    virtual pointer operator->() const = 0;
    virtual IDocumentIterator& operator++() = 0; // 前置递增
    virtual bool operator==(const IDocumentIterator& other) const = 0;
    virtual bool operator!=(const IDocumentIterator& other) const = 0;
};

步骤2:给IDocument添加迭代器接口

修改IDocument,添加返回适配后的迭代器的虚函数。这里用适配器类包装抽象迭代器,让它兼容STL迭代器语义(支持范围for循环):

// 迭代器适配器,封装抽象迭代器,提供值语义和STL迭代器特性
class DocumentIteratorAdapter {
private:
    std::unique_ptr<IDocumentIterator> m_impl;
public:
    using value_type = IDocumentIterator::value_type;
    using reference = IDocumentIterator::reference;
    using pointer = IDocumentIterator::pointer;
    using iterator_category = std::input_iterator_tag;
    using difference_type = std::ptrdiff_t;

    explicit DocumentIteratorAdapter(std::unique_ptr<IDocumentIterator> impl)
        : m_impl(std::move(impl)) {}

    // 拷贝构造:通过clone()实现多态拷贝
    DocumentIteratorAdapter(const DocumentIteratorAdapter& other)
        : m_impl(other.m_impl->clone()) {}

    DocumentIteratorAdapter& operator=(const DocumentIteratorAdapter& other) {
        if (this != &other) {
            m_impl.reset(other.m_impl->clone());
        }
        return *this;
    }

    // 移动语义默认支持
    DocumentIteratorAdapter(DocumentIteratorAdapter&&) = default;
    DocumentIteratorAdapter& operator=(DocumentIteratorAdapter&&) = default;

    // 转发操作到底层抽象迭代器
    reference operator*() const { return **m_impl; }
    pointer operator->() const { return m_impl->operator->(); }
    DocumentIteratorAdapter& operator++() {
        ++*m_impl;
        return *this;
    }
    DocumentIteratorAdapter operator++(int) {
        auto temp = *this;
        ++*m_impl;
        return temp;
    }

    bool operator==(const DocumentIteratorAdapter& other) const {
        return *m_impl == *other.m_impl;
    }
    bool operator!=(const DocumentIteratorAdapter& other) const {
        return !(*this == other);
    }
};

// 修改后的IDocument接口
class IDocument {
public:
    using Path = StdPath;
    using const_iterator = DocumentIteratorAdapter;

    virtual void InsertItem(const DocumentItem& item,
        std::optional<size_t> position = std::nullopt) = 0;

    virtual size_t GetItemsCount() const = 0;
    virtual const DocumentItem& GetItem(size_t index) const noexcept = 0;
    virtual void DeleteItem(size_t index) = 0;

    virtual const std::string& GetTitle() const = 0;
    virtual void SetTitle(const std::string& title) = 0;

    // 迭代器接口
    virtual const_iterator begin() const = 0;
    virtual const_iterator end() const = 0;

    virtual ~IDocument() = default;
};

步骤3:在派生类中实现迭代器

以HTMLDocument为例,实现具体的迭代器类,封装std::deque<DocumentItem>的迭代器,同时实现IDocument的begin()/end()方法:

class HTMLDocumentIterator : public IDocumentIterator {
private:
    std::deque<DocumentItem>::const_iterator m_iter;
public:
    explicit HTMLDocumentIterator(std::deque<DocumentItem>::const_iterator iter)
        : m_iter(iter) {}

    IDocumentIterator* clone() const override {
        return new HTMLDocumentIterator(m_iter);
    }

    reference operator*() const override { return *m_iter; }
    pointer operator->() const override { return &(*m_iter); }

    IDocumentIterator& operator++() override {
        ++m_iter;
        return *this;
    }

    bool operator==(const IDocumentIterator& other) const override {
        // 动态转换判断是否是同类型迭代器
        const auto* otherDerived = dynamic_cast<const HTMLDocumentIterator*>(&other);
        return otherDerived && m_iter == otherDerived->m_iter;
    }

    bool operator!=(const IDocumentIterator& other) const override {
        return !(*this == other);
    }
};

class HTMLDocument : public IDocument {
public:
    HTMLDocument() = default;

    template <typename StringT = std::string>
    HTMLDocument(StringT&& string)
        : m_title(std::forward<StringT>(string)) {}

    size_t GetItemsCount() const final { return m_items.size(); }
    const DocumentItem& GetItem(size_t index) const noexcept final { return m_items[index]; }
    void DeleteItem(size_t index) final { m_items.erase(m_items.begin() + index); }

    const std::string& GetTitle() const final { return m_title; }
    void SetTitle(const std::string& title) final { m_title = title; }

    void Save(const StdPath& path) const final; // 原有实现保持不变

    // 实现迭代器接口
    const_iterator begin() const final {
        return const_iterator(std::make_unique<HTMLDocumentIterator>(m_items.begin()));
    }
    const_iterator end() const final {
        return const_iterator(std::make_unique<HTMLDocumentIterator>(m_items.end()));
    }

private:
    std::string m_title = "Title";
    std::deque<DocumentItem> m_items;
    UndoManager m_undoManager{};
};

步骤4:实现输出到std::ostream的非成员函数

利用迭代器接口,轻松实现文档内容的输出:

#include <ostream>

std::ostream& operator<<(std::ostream& os, const IDocument& doc) {
    os << "Document Title: " << doc.GetTitle() << "\n";
    os << "Items:\n";
    for (const auto& item : doc) {
        // 假设DocumentItem有对应的operator<<实现,或根据需求自定义输出逻辑
        os << "- " << item << "\n";
    }
    return os;
}

关键优势说明

  • 封装性:完全隐藏了派生类使用的具体容器类型(比如std::deque),符合抽象接口的设计原则。
  • 兼容性:适配后的迭代器支持STL标准迭代器语义,可直接用于范围for循环、STL算法等。
  • 扩展性:新增其他文档类型(比如PDFDocument)时,只需要实现对应的具体迭代器类即可,无需修改抽象接口。

内容的提问来源于stack exchange,提问作者b442

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 03:45:36