C++设计模式——迭代器模式

C++设计模式 - 迭代器模式 知识的学习在于点滴记录,坚持不懈;知识的学习要有深度和广度,不能只流于表面,坐井观天;知识要善于总结,不仅能够理解,更知道如何表达! 目录设计模式概念迭代器模式简介迭代器模式代码示例 设计模式概念 设计模式简单来说就是在解决某一类问题场景时,有既定的,优秀的代码框架可以直接使用,与我们自己摸索出来的问题解决之道相比较,有以下优点可取: 代码更易于维护,代码的可读性,复用性,可移植性,健壮性会... 阅读详情

前言

最近非常感伤,总是怀念大学的日子,做梦的时候也常常梦到。梦到大学在电脑前傻傻的敲着键盘,写着代码,对付着数据结构与算法的作业;建立一个链表,遍历链表,打印链表。现在把那个时候声明的链表的头文件拿出来看看:

typedef struct tagNode
{
     int value;
     tagNode *pPre;
     tagNode *pNext;
}Node;

class CList
{
public:
     CList();
     CList(size_t n);
     ~CList();

     bool PushBack(int value);
     bool PopBack(int &value);
     bool Insert(int pos, int value);
     bool Delete(int pos);
     bool IsEmpty();
     int GetLength();

     void Print();

     // To iterate the list
     bool HasNext();
     int Next();

private:
     int m_iLength;
     Node *m_pCurrent;
     Node *m_pHead;
     Node *m_pTail;
};

再回头看看,自己写的代码都有点不认识了。是的,那个时候,就是直接将链表的创建和遍历都放在一类中,就是为了方便,直到那天看了迭代器设计模式,让我有了一次回过头来重新审视自己写过的代码,认识自己的不足的机会。

 

迭代器模式

在GOF的《设计模式:可复用面向对象软件的基础》一书中对迭代器模式是这样说的:提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示。

一个聚合对象,就是所谓的对象容器了;作为一个容器,都应该提供一种方法来让别人可以访问它的元素;但是,有的时候,我是不希望遍历容器的人知道我的容器是如何实现的;那该怎么办?就像我在大学那样实现的链表,只提供了从头到尾的遍历,如果我需要从尾到头的遍历呢?是不是我又要添加对应的方法了呢!!!容器的遍历方式千变万化,我们不知道需求是如何的,如果需求变了,那么我们的代码就会发生很大的改动,所以,我们需要去改变;对于上面的代码,当我对同一个链表对象进行多次遍历时,是不是就出现了m_pCurrent对象混乱的局面呢?是的,这一切的一切,都说明,我们必须去将一个容器的内部结构与它的遍历进行解耦,要是出现上面的情况时,我们就无法面对。就好比STL中的容器,它将容器中对象的实现和遍历很好的解耦了,所以,我们就无法知道它的内部是如何组织对象数据的,同时,我们也可以按照我们自己的想法去遍历容器,而不会出现任何差错。在我们的项目中使用迭代器模式就能很好的将容器对象的内部表示与对它的遍历进行解耦。接下来,我们再来详细的总结迭代器模式。

UML类图

Iterator:定义迭代器访问和遍历元素的接口;
ConcreteIterator:实现具体的迭代器;
Aggregate:定义的容器,创建相应迭代器对象的接口;
ConcreteAggregate:具体的容器实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例。

 

使用场合

  1. 访问一个聚合对象的内容而无需暴露它的内部表示;
  2. 支持对聚合对象的多种遍历(从前到后,从后到前);
  3. 为遍历不同的聚合结构提供一个统一的接口,即支持多态迭代。

 

作用

  1. 它支持以不同的方式遍历一个聚合,甚至都可以自己定义迭代器的子类以支持新的遍历;
  2. 迭代器简化了聚合的接口,有了迭代器的遍历接口,聚合本身就不再需要类似的遍历接口了。这样就简化了聚合的接口;
  3. 在同一个聚合上可以有多个遍历,每个迭代器保持它自己的遍历状态;因此,我们可以同时进行多个遍历。

 

代码实现

#include <iostream>
using namespace std;

typedef struct tagNode
{
     int value;
     tagNode *pNext;
}Node;

class JTList
{
public:
     JTList() : m_pHead(NULL), m_pTail(NULL){};
     JTList(const JTList &);
     ~JTList();
     JTList &operator=(const JTList &);

     long GetCount() const;
     Node *Get(const long index) const;
     Node *First() const;
     Node *Last() const;
     bool Includes(const int &) const;

     void Append(const int &);
     void Remove(Node *pNode);
     void RemoveAll();

private:
     Node *m_pHead;
     Node *m_pTail;
     long m_lCount;
};

class Iterator
{
public:
     virtual void First() = 0;
     virtual void Next() = 0;
     virtual bool IsDone() const = 0;
     virtual Node *CurrentItem() const  = 0;
};

class JTListIterator : public Iterator
{
public:
     JTListIterator(JTList *pList) : m_pJTList(pList), m_pCurrent(NULL){}

     virtual void First();
     virtual void Next();
     virtual bool IsDone() const;
     virtual Node *CurrentItem() const;

private:
     JTList *m_pJTList;
     Node *m_pCurrent;
};

JTList::~JTList()
{
     Node *pCurrent = m_pHead;
     Node *pNextNode = NULL;
     while (pCurrent)
     {
          pNextNode = pCurrent->pNext;
          delete pCurrent;
          pCurrent = pNextNode;
     }
}

long JTList::GetCount()const
{
     return m_lCount;
}

Node *JTList::Get(const long index) const
{
     // The min index is 0, max index is count - 1
     if (index > m_lCount - 1 || index < 0)
     {
          return NULL;
     }

     int iPosTemp = 0;
     Node *pNodeTemp = m_pHead;
     while (pNodeTemp)
     {
          if (index == iPosTemp++)
          {
               return pNodeTemp;
          }
          pNodeTemp = pNodeTemp->pNext;
     }
     return NULL;
}

Node *JTList::First() const
{
     return m_pHead;
}

Node *JTList::Last() const
{
     return m_pTail;
}

bool JTList::Includes(const int &value) const
{
     Node *pNodeTemp = m_pHead;
     while (pNodeTemp)
     {
          if (value == pNodeTemp->value)
          {
               return true;
          }
          pNodeTemp = pNodeTemp->pNext;
     }
     return false;
}

void JTList::Append(const int &value)
{
     // Create the new node
     Node *pInsertNode = new Node;
     pInsertNode->value = value;
     pInsertNode->pNext = NULL;

     // This list is empty
     if (m_pHead == NULL)
     {
          m_pHead = m_pTail = pInsertNode;
     }
     else
     {
          m_pTail->pNext = pInsertNode;
          m_pTail = pInsertNode;
     }
     ++m_lCount;
}

void JTList::Remove(Node *pNode)
{
     if (pNode == NULL || m_pHead == NULL || m_pTail == NULL)
     {
          return;
     }

     if (pNode == m_pHead) // If the deleting node is head node
     {
          Node *pNewHead = m_pHead->pNext;
          m_pHead = pNewHead;
     }
     else
     {
          // To get the deleting node's previous node
          Node *pPreviousNode = NULL;
          Node *pCurrentNode = m_pHead;
          while (pCurrentNode)
          {
               pPreviousNode = pCurrentNode;
               pCurrentNode = pCurrentNode->pNext;
               if (pCurrentNode == pNode)
               {
                    break;
               }
          }

          // To get the deleting node's next node
          Node *pNextNode = pNode->pNext;

          // If pNextNode is NULL, it means the deleting node is the tail node, we should change the m_pTail pointer
          if (pNextNode == NULL)
          {
               m_pTail = pPreviousNode;
          }

          // Relink the list
          pPreviousNode->pNext = pNextNode;
     }

     // Delete the node
     delete pNode;
     pNode = NULL;
     --m_lCount;
}

void JTList::RemoveAll()
{
     delete this;
}

void JTListIterator::First()
{
     m_pCurrent = m_pJTList->First();
}

void JTListIterator::Next()
{
     m_pCurrent = m_pCurrent->pNext;
}

bool JTListIterator::IsDone() const
{
     return m_pCurrent == m_pJTList->Last()->pNext;
}

Node *JTListIterator::CurrentItem() const
{
     return m_pCurrent;
}

int main()
{
     JTList *pJTList = new JTList;
     pJTList->Append(10);
     pJTList->Append(20);
     pJTList->Append(30);
     pJTList->Append(40);
     pJTList->Append(50);
     pJTList->Append(60);
     pJTList->Append(70);
     pJTList->Append(80);
     pJTList->Append(90);
     pJTList->Append(100);

     Iterator *pIterator = new JTListIterator(pJTList);

     // Print the list by JTListIterator
     for (pIterator->First(); !pIterator->IsDone(); pIterator->Next())
     {
          cout<<pIterator->CurrentItem()->value<<"->";
     }
     cout<<"NULL"<<endl;

     // Test for removing
     Node *pDeleteNode = NULL;
     for (pIterator->First(); !pIterator->IsDone(); pIterator->Next())
     {
          pDeleteNode = pIterator->CurrentItem();
          if (pDeleteNode->value == 100)
          {
               pJTList->Remove(pDeleteNode);
               break;
          }
     }

     // Print the list by JTListIterator
     for (pIterator->First(); !pIterator->IsDone(); pIterator->Next())
     {
          cout<<pIterator->CurrentItem()->value<<"->";
     }
     cout<<"NULL"<<endl;

     delete pIterator;
     delete pJTList;

     return 0;
}

代码中实现了一个单向链表,将链表与迭代器解耦。对于多态迭代,添加抽象类AbstractJTList,声明如下:

class AbstractJTList
{
public:
     virtual Iterator *GetIterator() const = 0;
};

类JTList继承该抽象类,并实现GetIterator,如下:

 

Iterator *JTList::GetIterator() const
{
     return new JTListIterator(this);
}

好了,这样的话,在客户端就不用去new JTListIterator了,只需要这样:

Iterator *pIterator = pJTList->GetIterator();

这就完全好了;但是,这样又出现另外一个问题,我在GetIterator中new了一个JTListIterator,对于客户端来说,我并不知道这个new操作的存在,就会出现客户端不会去释放这个new开辟的内存,那么如何实现这个内存的自动释放呢。好了,就结合迭代器模式,再将之前总结的RAII机制再实际运用一次。

根据RAII机制,需要将这个迭代器进行封装,让它具有自动释放的功能,就得借助另一个类,如下:

class IteratorPtr
{
public:
     IteratorPtr(Iterator *pIterator) : m_pIterator(pIterator){}
     ~IteratorPtr() { delete m_pIterator; }

     Iterator *operator->(){ return m_pIterator; }
     Iterator &operator*() { return *m_pIterator; }

private:
     IteratorPtr(const IteratorPtr &);
     IteratorPtr &operator=(const IteratorPtr &);
     void *operator new(size_t size);
     void operator delete(void *);

private:
     Iterator *m_pIterator;
};

我们在使用的时候,就像下面这样:

IteratorPtr pIterator(pJTList->GetIterator());

这样就省去了释放迭代器的麻烦了。

 

总结

迭代器模式是一个很经典的模式。但是,就是因为它太经典了,如果每次都要程序员去重复造轮子,就有点说不过去了,所以,现在基本成型的类库,都非常好的实现了迭代器模式,在使用这些类库提供的容器时,并不需要我们亲自去实现对应的迭代器;就好比STL了。但是话又说回来了,如此经典的东西,你不去学习是不是很可惜啊;是吧,在当今社会,技多不压身。好了,永远记住,设计模式是一种思想,并不是一层不变的,一种思想,你懂的。

C++设计模式——迭代器模式(iterator pattern) 一、原理讲解 1.1意图 提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。 1.2应用场景 访问一个聚合对象的内容而无需暴露它的内部表示; 支持对聚合对象的多种遍历; 为遍历多种不同的聚合结构提供一个统一的接口(支持多态迭代); 1.3结构图(UML图) 1.4代码实现步骤 a1 定义接口迭代器类Iterator,定义四个接口函数first(),next(),current(),isEnd(); a2 定义一个抽象聚合类Aggregate(),定义四个接口函数push() 阅读详情

相关推荐

大话设计模式迭代器模式c++实现

迭代器模式 其他二十三种设计模式 #include<iostream> #include<vector> #include<string> //迭代模式 //--基本看的别人的 using namespace std; typedef string object; //抽象迭代器类 class Iterator { public: virtual object First() = 0; virtual object Next() = 0; virtual boo

wode_0828的博客 229

设计模式第16讲——迭代器模式(Iterator)

迭代器模式是一种行为型设计模式。一、什么是迭代器模式 二、角色组成 三、 优缺点 四、应用场景 4.1 生活场景 4.2 java场景 五、代码实现5.0 UML类图 5.1 Student——学生实体类5.2 StudentIterator——抽象迭代器(Iterator)5.3 StudentListIterator——具体迭代器(Concrete iterator)5.4 StudentAggregate——抽象聚合器(Aggregate)5.5 ClassList

让~学习~成为一种习惯(橡 皮 人の技术博客) 1万+

C++设计模式16:迭代器模式

迭代器模式是一种行为型设计模式,它允许你在不暴露集合底层表现形式(列表、栈和树等)的情况下遍历集合中所有的元素。迭代器模式通过提供一个迭代器来实现这一点,该迭代器用于跟踪集合中的当前元素,并知道如何遍历到下一个元素。在迭代器模式中,集合类通常会实现一个名为 iterator 的方法,该方法返回一个迭代器对象。迭代器对象通常会实现两个方法:next 和 hasNext。next 方法返回集合中的下一个元素,而 hasNext 方法则用于检查是否还有更多的元素需要遍历。

琅嬛福地 1612

设计模式-迭代器模式(Iterator)

迭代器模式是一种设计模式,它用于提供一种方法来访问一个容器对象中的各个元素,而又不暴露该对象的内部表示。在迭代器模式中,我们定义一个抽象的迭代器类,它包含两个方法:一个是hasNext()方法,用于判断是否还有下一个元素;另一个是next()方法,用于获取下一个元素。然后,每个容器类都实现自己的迭代器类,以访问容器中的元素。

miaoyl的博客 2496

如何用 Python 实现自动化测试?详细步骤和代码示例

Python自动化测试实战指南 本文系统介绍了使用Python构建自动化测试体系的完整方案。主要内容包括: 环境搭建:Python 3.8+虚拟环境配置,安装pytest、Selenium等核心测试库 分层测试实现: 单元测试:通过计算器示例演示pytest断言和异常测试 API测试:使用requests库进行RESTful接口验证 UI测试:Selenium WebDriver实现网页交互测试 高级特性: 使用fixture管理测试依赖 参数化实现数据驱动测试 生成HTML和Allure测试报告 GitH

m0_64424835的博客 448

Gazebo仿真平台模型搭建与修改

ROS里踩过的坑(一)Gazebo仿真平台模型搭建与修改文件讲解Models 文件World文件Launch文件模型编辑可视化编辑配置文件编辑模型展示如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出...

Redglass的博客 1万+

迭代器模式

定义 迭代器(Iterator)模式的定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。迭代器模式是一种对象行为型模式。 在学习Java集合的时候,我们要访问容器内的对象,就要知道容器的结构,如ArrayList要用get来获取对象,而set只能用迭代器或for each循环。所以,当容器类型很多时,使用是不方便的,我们需要一种接口,这个接口可以遍历容器的对象,而...

Endeavor_G的博客 1993

无人机WIFI集群组网技术详解及成本分析

无人机WIFI集群组网技术是指利用无人机作为移动平台,通过集成高性能的WIFI模块,实现多架无人机之间以及无人机与地面控制站之间的无线通信组网。在协议层面,无人机WIFI集群组网需要遵循标准的无线通信协议,如IEEE 802.11系列(即WIFI标准),同时可能还需定制或优化路由协议、媒体访问控制(MAC)协议等,以确保高效、可靠的数据传输。数据安全与隐私保护:无人机集群组网中传输的数据可能涉及敏感信息,需采取加密、认证等安全措施,确保数据传输的安全性和用户隐私的保护。根据性能不同,价格差异较大。

专注多旋翼、复合翼无人机技术研究,从事低空经济行业。 1582

设计模式之【迭代器模式】,对集合访问的统一

迭代器模式(Iterator Pattern)又称为游标模式(Cursor Pattern),它提供一种顺序访问集合/容器对象元素的方法,而又无需暴露集合内部表示。迭代器模式可以为不同的容器提供一致的遍历行为,而不用关心容器内容元素组成结构,属于行为型模式。迭代器模式的本质是抽离集合对象迭代行为到迭代器中,提供一致访问接口。

秃了也弱了 1535

gesp c++ 八级知识点

gesp c++ 八级知识点

MMD.然大大 2233

Java 处理地理信息数据[DEM TIF文件数据获取高程]

EPSG 是一个国际标准,用于唯一标识地理坐标系和投影坐标系。

两点 1275

Java设计模式之行为型:迭代器模式

迭代器模式提供一种访问集合中的各个元素,而不暴露其内部表示的方法。将在元素之间游走的职责交给迭代器,而不是集合对象,从而简化集合容器的实现,让集合容器专注于在它所应该专注的事情上,更加符合单一职责原则。

张维鹏的博客 1万+

中兴交换机配置SSH登录

SSH是在传统的Telnet协议基础上发展起来的一种安全的远程登录协议。发送和接收数据时, SSH会自动对数据进行加密及解密。相比于Telnet,SSH能防止“中间人”攻击、DNS欺骗、IP 欺骗等,无论是在认证方式或者数据传输的安全性上都有很大的提高。SSH用户认证方式有3种,分别为本地(Local)认证、RADIUS认证和TACACS+认证。以表规划数据为例,说明配置SSH登录的步骤。

才怪的博客 7922

C++ 设计模式:迭代器模式(Iterator Pattern)

C++ 设计模式:迭代器模式(Iterator Pattern)

冀晓武的博客 859

低代码组件开发——OpenClaw智能助手的组件设计与开发(2026技术版)

OpenClaw智能助手的低代码组件开发平台采用分层架构设计,包含组件定义、注册、配置、渲染和生命周期管理等核心模块。该系统支持UI、功能、数据和集成四种组件类型,强调可重用性、可配置性和可扩展性。组件定义通过TypeScript、React等技术实现组件结构和行为的描述,组件注册中心基于Node.js和MongoDB管理组件版本。该平台通过React、Redux等技术实现组件渲染和状态管理,并提供完整的生命周期管理机制。这种架构设计显著提升了智能助手应用的开发效率和质量。

一键难忘的博客 407

8.时间序列预测

目标:使用历史数据预测未来的销售额。通过以上步骤,可以在Excel中有效进行预测分析,帮助做出更准确的决策通过考虑不同时期数据的影响,可以显著提高预测的准确性。结合多种方法和模型,可以灵活应对复杂的市场环境和变化。

学习,输出==》再学习再输出 891

C++设计模式十一--IteratorPattern(迭代器模式)

C++ 设计模式 IteratorPattern 迭代器模式

雪狼的博客 551

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

【c++设计模式18】行为模式3:迭代器模式(Iterator Pattern)

迭代器模式定义了一个迭代器接口(Iterator),该接口包含了对容器对象(如列表或集合)进行遍历的方法。具体容器对象(Concrete Collection)实现了迭代器接口,并且提供了创建迭代器对象的方法。迭代器对象(Iterator Object)封装了对容器对象的访问和遍历逻辑。

junxuezheng的博客 1130

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

c++ 设计模式 迭代器模式

基本类型(和指针的定义方法一致)类型 *迭代器变量名;举例int *iter;容器类型(以string为例,后续会详细学容器)

2403_84775671的博客 1067

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

C++设计模式行为模式———迭代器模式

当集合背后为复杂的数据结构, 且你希望对客户端隐藏其复杂性时 (出于使用便利性或安全性的考虑), 可以使用迭代器模式。迭代器封装了与复杂数据结构进行交互的细节, 为客户端提供多个访问集合元素的简单方法。这种方式不仅对客户端来说非常方便, 而且能避免客户端在直接与集合交互时执行错误或有害的操作, 从而起到保护集合的作用。声明迭代器接口。该接口必须提供至少一个方法来获取集合中的下个元素。但为了使用方便, 你还可以添加一些其他方法, 例如获取前一个元素、 记录当前位置和判断迭代是否已结束。

无敌岩雀的博客 1975

C++ 设计模式之迭代器模式

C++ 设计模式之迭代器模式

L_qingting的博客 895

Qt C++设计模式->迭代器模式

迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供了一种方法顺序访问集合对象中的元素,而无需暴露集合的底层表示。迭代器允许你遍历不同的数据结构,比如数组、链表或其他容器,具有统一的接口。在Qt C++中,使用迭代器可以更简洁、安全地遍历QList、QVector、QMap等容器。Qt提供了一些内建的迭代器类,例如等。

m0_46494667的博客 1023

c++设计模式:迭代器模式(Iterator Pattern)

定义:迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。 场景:我们有两个聚合A和B,有一个聚合管理的类,包含聚合A和B,而这个管理类的功能就是依次遍历它所包含的聚合中的元素并打印到控制台上。每个聚合中的元素存放的方式是不同的,有的用的是数组,有的可能是vector或者list等,因此我么为每个聚合定义了一个迭代器,管理类只需要依次获取各聚合的迭代器,使用迭代器

编程小生的专栏 1435

基于C++的《Head First设计模式》笔记——迭代器模式

本文通过餐厅与煎饼屋合并的案例,探讨了如何用C++实现迭代器模式。作者首先分析了两种不同菜单实现方式(vector和数组)带来的遍历问题,然后详细展示了如何为两种菜单类分别实现迭代器,包括运算符重载等关键实现细节。通过迭代器模式,成功将遍历逻辑封装到迭代器对象中,简化了聚合接口并遵循单一职责原则。最终的女招待类通过统一的迭代器接口实现了菜单遍历,解决了代码重复和维护问题。文章还指出C++迭代器实现与Java的差异,并讨论了进一步抽象为统一接口的可能性。

Yu_Lijing的博客 826

C++ QT设计模式:迭代器模式

迭代器模式(Iterator Pattern)是一种设计模式,提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。迭代器模式将遍历元素的责任封装到一个独立的迭代器对象中,使得聚合对象和遍历算法分离开来。:定义了访问和遍历元素的接口。:实现了 Iterator接口,具体定义了如何实现遍历算法。:定义创建迭代器对象的接口。:实现了聚合器接口,具体定义了 Aggregate 创建相应迭代器对象的逻辑。

buhuiCyvyan的博客 480

23种设计模式C++实例之迭代器模式

【代码】23种设计模式C++实例之迭代器模式。

寒风的技术博客 1272
上一篇: C++设计模式——解释器模式
下一篇: C++设计模式——中介者模式
CoderAldrich
博客等级 码龄12年 431粉丝 24原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值