泛型编程的差异抽象思想

个人观点:

在实现相应的迭代器时,我们多传入了两个模版参数来实现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 实现中非常精妙且标准的一个技巧。

相关推荐
map_vis_3d1 天前
JSAPIThree 加载简单点图层学习笔记:SimplePoint 散点可视化
笔记·学习·信息可视化·mapvthree·jsapithree·simplepoint·点图层
蓝田生玉1231 天前
BEVFormer论文阅读笔记
论文阅读·笔记
西瓜堆1 天前
提示词工程学习笔记: 工程技术行业提示词推荐
笔记·学习
LawrenceLan1 天前
Flutter 零基础入门(十一):空安全(Null Safety)基础
开发语言·flutter·dart
txinyu的博客1 天前
解析业务层的key冲突问题
开发语言·c++·分布式
码不停蹄Zzz1 天前
C语言第1章
c语言·开发语言
行者961 天前
Flutter跨平台开发在OpenHarmony上的评分组件实现与优化
开发语言·flutter·harmonyos·鸿蒙
阿蒙Amon1 天前
C#每日面试题-Array和ArrayList的区别
java·开发语言·c#
SmartRadio1 天前
ESP32添加修改蓝牙名称和获取蓝牙连接状态的AT命令-完整UART BLE服务功能后的完整`main.c`代码
c语言·开发语言·c++·esp32·ble
wxr06161 天前
GOF笔记
笔记·适配器·ooad