泛型编程的差异抽象思想

个人观点:

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

相关推荐
Sunsets_Red1 天前
乘法逆元的 exgcd 求法
c++·学习·数学·算法·c#·密码学·信息学竞赛
qcwl661 天前
深入理解Linux进程与内存 学习笔记#1
笔记·学习
xuhaoyu_cpp_java1 天前
Servlet学习
java·笔记·学习
左左右右左右摇晃1 天前
JVM 整理(二) 类加载器
jvm·笔记
I love studying!!!1 天前
python项目: 下载数据
开发语言·python
米啦啦.1 天前
函数模板,namespace名字空间,动态内存管理,C++11新特性,
c++·动态内存管理·函数模板·c++新特性·名字空间
不只会拍照的程序猿1 天前
《嵌入式AI筑基笔记03:Python流程控制,从C的严谨到Python的简洁》
c语言·开发语言·笔记·python
问今域中1 天前
java技术史001:EJB 侵入性的历史阵痛与 Spring 的突围
java·开发语言·rpc
He BianGu1 天前
【笔记】在WPF中GiveFeedbackEventHandler的功能和应用场景详细介绍
笔记·wpf
BUG创建者1 天前
openlayers上跟据经纬度画出轨迹
开发语言·javascript·vue·html