frankfan的胡思乱想

学海无涯,回头是岸

迭代器

迭代器及其实现原理
『容器迭代器』以及迭代器的实现原理。

在详细讨论迭代器的实现细节时,我们需要事先搞清楚几个前置问题:

1、基于堆的数据结构怎么表述一个节点的位置信息?

2、常规的表达位置的方式有什么弊端?

3、为什么需要迭代器?

4、迭代器的是怎么设计的?


基于堆的数据结构节点都是分配在堆上的,用指针存储其值。

只要不是内存严格要求存在先后顺序的数据结构,其结构都是通过分配在堆上的数据节点根据指针进行联系。那么在寻找数据或者删除以及插入数据是,怎么表述某个数据的位置就成了一个问题。

#include <iostream>
#include "MyList"
using namespace std;
int main(){
    MyList mlist;
    Node *node = new Node(11);
    Node *node2 = new Node(22);
    Node node3 = new Node(33);
    mlist.push_back(node);
    mlist.push_back(node2);
    mlist.push_back(node3);
    
    mlist.insert(node2,Node(44));
    
    mlist.remove(node3);
    return 0;
}

诚然,可以通过上述方式,用指针的方式来表述数据需要插入(或者删除)的位置

但是中方式存在明显的几个弊端:

弊端1、不能直接插入原始数据,只能插入指针

弊端2、外界可以直接操作插入的指针数据

这2个弊端就足以让这种操作数据结构的方式宣告失败。


那么在删除或者插入数据时能不能直接指定节点的索引值呢?

#include <iostream>
#include "MyList"
using namespace std;
int main(){
    MyList list;
    list.push_back(11);
    list.push_back(22);
    list.push_back(33);
    
    list.insert(1,55);
    list.remove(2);
    
    return 0;
}

查过索引值直接表示插入或者删除的位置,这种方式也有几个弊端:

弊端1、这样一些数据结构的优势便丧失,比如链表的插入与删除时间复杂度都是O(1),但是当通过指定索引值的方式使得其时间复杂度变成了O(n)

弊端2、链表这样的数据结构可以指定索引,但是如『树』『图』等数据结构并非线性结构,无从定义索引值

因此,无论是使用裸指针还是索引值都无法很好的表示数据结构容器中的位置信息。

因此,便使用了另一个机制『迭代器』


迭代器

其实第一种使用指针的方式本质上是可行的,但从设计模式的角度上而言并不能被接受。

迭代器其实是设计模式中『门面模式』的实现。

所谓门面模式:

#include <iostream>
using namespace std;
class Factor;
class Node{
public:
    friend class Factor;
    void showvalue(){
        cout<<value<<endl;
    }
private:
    int value;
};
class FactorNode{
public:
    FactorNode(){
        node = new Node;
    }
    void showvalue(){
        if(node>1){
            node->showvalue();
        }
    }
private:
    Node *node;
};
int main(){
    
    Factor factor;
    factor.showvalue;
    return 0;
}

用另一个类『包裹』住另一个类,宿主类就是所谓的门面,这种设计模式就是门面模式。

这种设计模式的核心是对『在计算机中任何问题都可以通过加一个中间层得以解决』

这句名言的生动实践。

同理,在第一种方案中,将表示位置的指针通过门面模式,让另一个类对这个指针对象进行包裹,这样用户就无法直接操作指针,而是通过宿主类来操作。这个宿主类就是所谓的『迭代器』


#pragma once
#include <iostream>

class MyList;
class Node;

class MyList
{
public:
	MyList();
	~MyList();
public:
	class Iterator {
	
	public:
		Iterator(Node*);
		~Iterator();
	public:
		friend class MyList;
	public:
		Iterator& operator++();
		void operator++(int);
		Iterator& operator--();
		void operator--(int);
		bool operator==(const Iterator &obj);
		bool operator !=(const MyList::Iterator &obj);
		int& operator*();
		int* operator->();

	private:
		Node *wrapNode;
	};
public: 
	void push_back(int);
	unsigned int size();
	Iterator insertValue(Iterator,int);
	Iterator removeValue(Iterator);
	Iterator begin();
	Iterator end();

public:
	friend std::ostream & operator<<(std::ostream &os,const MyList &obj);
private:
	int nodeCount;
	Node *headerNode;
	Node *endNode;
};

class Node {
public:
	Node(int v = 0);
	~Node();
public:
	friend class MyList;
	friend std::ostream & operator<<(std::ostream & os, const MyList & obj);
	friend class MyList::Iterator;
private:
	int value;
	Node *nextnode;
	Node *prenode;
	bool isHeaderNode;
};

上面准确描述了迭代器与MyList这个数据结构的关系。

迭代器是MyList的内部类,通过迭代器这个类封装了节点指针,这样就避免了用户直接操作裸指针了。

也就是说迭代器类是节点类Node的宿主。

MyList这个数据结构则需要提供获取迭代器的方式。

#include "pch.h"
#include "MyList.h"
#include <iostream>
#include <assert.h>

MyList::MyList()
{
	nodeCount = 0;
	headerNode = new Node;
	headerNode->isHeaderNode = true;
	headerNode->value = 0;

	endNode = new Node;
	endNode->value = 0;
	endNode->nextnode = nullptr;
	headerNode->nextnode = endNode;
	endNode->prenode = headerNode;
}


MyList::~MyList()
{
	Node *lastnode = endNode->prenode;
	while (lastnode->prenode != nullptr)
	{
		Node *recodenode = lastnode;
		lastnode = lastnode->prenode;
		delete recodenode;
	}
	delete endNode;
	delete headerNode;
}

void MyList::push_back(int data)
{
	Node *node = new Node;
	node->value = data;
	
	Node *lastnode = headerNode;
	while (lastnode->nextnode->nextnode != nullptr)
	{
		lastnode = lastnode->nextnode;
	}
	lastnode->nextnode = node;
	node->prenode = lastnode;
	node->nextnode = endNode;
	endNode->prenode = node;
	
	nodeCount += 1;
}

unsigned int MyList::size()
{
	return nodeCount;
}

MyList::Iterator MyList::insertValue(MyList::Iterator it, int value)
{
#if 1
	Node *node = new Node;
	node->value = value;

	Node *preNode = it.wrapNode->prenode;

	node->nextnode = it.wrapNode;
	it.wrapNode->prenode = node;

	preNode->nextnode = node;
	node->prenode = preNode;

	nodeCount += 1;
	return Iterator(node);
#endif
}

MyList::Iterator MyList::removeValue(MyList::Iterator it)
{
	assert(it.wrapNode->nextnode != nullptr);
	it.wrapNode->prenode->nextnode = it.wrapNode->nextnode;
	it.wrapNode->nextnode->prenode = it.wrapNode->prenode;

	Node *nowNode = it.wrapNode->nextnode;
	delete it.wrapNode;

	nodeCount -= 1;
	return Iterator(nowNode);
}

MyList::Iterator MyList::begin()
{
	assert(nodeCount != 0);
	return headerNode->nextnode;
}

MyList::Iterator MyList::end()
{
	return endNode;
}

Node::Node(int v)
{
	value = v;
	nextnode = nullptr;
	prenode = nullptr;
	isHeaderNode = false;
}


Node::~Node()
{
}

std::ostream & operator<<(std::ostream & os, const MyList & obj)
{
	Node *node = obj.headerNode->nextnode;
	while (node->nextnode != nullptr)
	{
		os << node->value << " ";
		node = node->nextnode;
	}
	os << std::endl;
	return os;
}

MyList::Iterator::Iterator(Node *node = nullptr)
{
	wrapNode = node;
}

MyList::Iterator::~Iterator()
{
	
}

MyList::Iterator & MyList::Iterator::operator++()
{
	assert(wrapNode->nextnode != nullptr);
	wrapNode = wrapNode->nextnode;

	return *this;
}

void MyList::Iterator::operator++(int)
{
	assert(wrapNode->nextnode != nullptr);
	wrapNode = wrapNode->nextnode;
}

MyList::Iterator & MyList::Iterator::operator--()
{
	assert(wrapNode->isHeaderNode == false);
	wrapNode = wrapNode->prenode;
	return *this;
}

void MyList::Iterator::operator--(int)
{
	assert(wrapNode->isHeaderNode == false);
	wrapNode = wrapNode->prenode;
}

bool MyList::Iterator::operator==(const MyList::Iterator & obj)
{
	 return wrapNode == obj.wrapNode;
}

bool MyList::Iterator::operator!=(const MyList::Iterator & obj)
{
	return  wrapNode != obj.wrapNode;;
}

int & MyList::Iterator::operator*()
{
	assert(wrapNode->nextnode != nullptr);
	return wrapNode->value;
}

#if 1
int * MyList::Iterator::operator->()
{
	return &wrapNode->value;
}
#endif

MyList需要提供相关的操作数据的接口,而迭代器则需要提供操作节点的接口,++ -- 等操作本质就是在数据结构的指针链条上进行移动,此外还需要重载相关运算符,是的操作更加『原生』

posted on 2021-12-28 09:28  shadow_fan  阅读(43)  评论(0编辑  收藏  举报

导航