泛型编程的差异抽象思想

个人观点:

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

相关推荐
云泽8084 分钟前
Visual Studio 2026 新手保姆级教程:环境搭建、界面优化与编译器配置详解
开发语言·c++·c·开发工具·visual studio
2zcode1 小时前
基于MATLAB深度学习的乳腺钼靶影像乳腺癌智能诊断系统设计与实现
开发语言·深度学习·matlab·乳腺癌
MoonBit月兔1 小时前
错过大赛报名?MoonBit 开源大赛- 8 月黑客松正式开启
开发语言·ai-native·moonbit
fqbqrr1 小时前
2607d,C++细节比不上d
c++·d
^yi1 小时前
【C++】继承的那些事儿~
c++·继承·菱形继承·隐藏·final·继承原理·多继承的指针偏移
weixin_538601971 小时前
智能体测开Day31
开发语言·python
geovindu1 小时前
java: Facade Pattern
java·开发语言·后端·外观模式·结构型模式
listening7771 小时前
HarmonyOS 6.1 元服务深度优化:从“秒开”到“常驻”的极致体验
java·开发语言·spring
布朗克1681 小时前
Go 入门到精通-29-测试与基准
开发语言·后端·golang·测试与基准
格林威2 小时前
工业相机Chunk功能全解析:图像嵌入时间戳、编码器元数据(附堡盟C#代码)
开发语言·人工智能·数码相机·计算机视觉·c#·视觉检测·工业相机