泛型编程的差异抽象思想

个人观点:

在实现相应的迭代器时,我们多传入了两个模版参数来实现iterator和const_iterator的代码复用

这使用了泛型编程的抽象差异思想:

当我们的两个实现有很多相似重复的地方,而差异仅在一些返回值或者执行方式时,我们可以考虑这种方式来实现代码复用--->引入泛型模版来来掩盖差异--->当传入不同的模版时我们就会出现不同的实现

讲解

简单来说,引入 RefPtr 这两个额外模板参数,是为了把"返回值类型"当作参数传进去 ,从而用同一份代码模版,同时生成"普通迭代器"和"Const 迭代器"。

1. 为什么要这么做?

如果没有这两个额外的参数,为了实现 iterator(可读写)和 const_iterator(只读),你需要写两个几乎一模一样的类。

我们来看一下如果不使用模板技巧,代码会变成什么样(冗余写法):

复制代码
// 1. 普通迭代器:允许修改数据
template<class T>
struct Iterator {
    Node<T>* _node;
    
    // 返回 T&,允许修改
    T& operator*() { return _node->_val; }
    
    // 返回 T*,允许修改
    T* operator->() { return &_node->_val; }
};

// 2. Const迭代器:不允许修改数据
template<class T>
struct ConstIterator {
    Node<T>* _node; // 这里的指针本身还是普通指针,因为迭代器要能跑
    
    // 【区别在这里】:返回 const T&
    const T& operator*() { return _node->_val; }
    
    // 【区别在这里】:返回 const T*
    const T* operator->() { return &_node->_val; }
};

发现: 这两个类,除了 operator*operator->返回类型 不同(一个是 T&/T*,一个是 const T&/const T*),其他代码(构造函数、++--!===)是完全一样的!

复制粘贴会导致代码维护困难:如果你修改了 Iterator 的逻辑,还得记得去改 ConstIterator

2. 解决方案:把"差异"抽象成参数

为了复用代码,我们把变化的返回类型提取出来,作为模板参数传给迭代器类。

我们需要的不仅仅是 T(数据类型),还需要告诉编译器:

  1. 解引用 * 时,返回什么类型?(是 T& 还是 const T&?)------ 这就是 Ref
  2. 箭头 -> 时,返回什么类型?(是 T* 还是 const T*?)------ 这就是 Ptr

3. 代码实现:一石二鸟

现在,我们可以只写一个类模板:

复制代码
// template<数据类型, 引用的返回类型, 指针的返回类型>
template<class T, class Ref, class Ptr>
struct ListIterator {
    typedef ListIterator<T, Ref, Ptr> Self;
    Node<T>* _node;

    // ... 构造函数等 ...

    // 使用 Ref 参数作为返回类型
    Ref operator*() {
        return _node->_val;
    }

    // 使用 Ptr 参数作为返回类型
    Ptr operator->() {
        return &_node->_val;
    }
    
    Self& operator++() {
        _node = _node->_next;
        return *this;
    }
    
    bool operator!=(const Self& s) {
        return _node != s._node;
    }
};

4. 如何使用?(在容器类中)

在你的 listvector 类中,你只需要通过传递不同的参数来定义两种迭代器:

复制代码
template<class T>
class list {
public:
    typedef Node<T> Node;

    // 1. 普通迭代器:传入 T& 和 T*
    // Ref = T&, Ptr = T*
    typedef ListIterator<T, T&, T*> iterator;

    // 2. Const迭代器:传入 const T& 和 const T*
    // Ref = const T&, Ptr = const T*
    typedef ListIterator<T, const T&, const T*> const_iterator;

    // ... begin() 和 end() ...
};

5. 总结

为什么除了 class T 还需要 RefPtr

  • class T:决定了节点里存的是 int 还是 string
  • class Ref:决定了 *it 返回的是可读写引用 (T&) 还是只读引用 (const T&)。
  • class Ptr:决定了 it-> 返回的是可读写指针 (T*) 还是只读指针 (const T*)。

通过这三个参数的组合,编译器会在编译时自动为你生成两份代码,一份用于普通访问,一份用于只读访问,既保证了类型安全(const 正确性),又避免了写两遍重复的逻辑。这是 C++ STL 实现中非常精妙且标准的一个技巧。

相关推荐
zqy02271 小时前
python安装与环境配置
开发语言·python
客梦1 小时前
Java教室管理系统
java·笔记
star learning white1 小时前
xmC语言10
c语言·开发语言
拼好饭和她皆失1 小时前
Java学习---Arrays类
java·开发语言·学习
Dev7z1 小时前
基于MATLAB小波变换的音频水印算法研究与实现
开发语言·matlab·音视频
代码游侠1 小时前
学习笔记——GDB调试工具
linux·开发语言·笔记·学习
hetao17338371 小时前
2025-12-02~03 hetao1733837的刷题记录
c++·算法
QT 小鲜肉1 小时前
【孙子兵法之中篇】007. 孙子兵法·军争篇
人工智能·笔记·读书·孙子兵法