实现对映射进行迭代的类迭代器

implementing class iterator that iterates over a map

本文关键字:迭代器 迭代 映射 实现      更新时间:2023-10-16

如何创建一个类迭代器,在不导致内存泄漏的情况下对通用映射节点进行迭代?我的映射有一个名为"pair"的类,它是泛型的,有一个值和一个键。还有一个名为node的类,它包含一对指向下一个节点和前一个节点的指针。映射本身包含头节点。以及地图大小。。这个代码工作正常,我的问题是它有内存泄漏。因为迭代器是一个类(我们被要求实现为一个类,而不仅仅是一个节点),并且它包含一个指向具有对的node的指针。我们不能通过删除节点来删除对,只要我们想使用迭代器析构函数cause来完全删除对和导致映射中元素丢失的节点。你能给我一些建议吗?如何在没有内存泄漏的情况下正确实现?非常重要的是:map函数end()应该返回一个不是map元素之一的随机值(这就是我将其设置为NULL的原因)

template <class KeyType, class ValueType, class CompareFunction = std::less<KeyType> >
class MtmMap {
public:
    class Pair {
    public:
        Pair(const KeyType& key, const ValueType& value)
            : first(key), second(value) {}
        Pair()
            : first(NULL), second(NULL) {}
        ~Pair(){}
        const KeyType first;
        ValueType second;
        bool operator==(const Pair& pair)const{
            if(pair.first==this->first&&pair.second==this->second){
                return true;
            }
            return false;
        }
        bool operator!=(const Pair& pair)const{
            if(this==pair){
                return false;
            }
            return true;
        }
        Pair& operator=(const Pair& pair){
            if(this==&pair){
                return *this;
            }
            this->first=pair.first;
            this->second=pair.second;
            return *this;
        }
        Pair& operator=(ValueType val){
            this->second=val;
            return *this;
        }
        const KeyType& getFirst(){
            KeyType* keyPointer=&first;
            return (first);
        }
        ValueType getSecond(){
            ValueType* valPoiner= &second;
            return (valPoiner);
        }
    };
    class Node {
        public:
            Pair* element;
            //int plc;
            Node* next;
            Node* before;
            Node() :element(), next(NULL),before(NULL){}
            Node(const Node& copyNode) :
                    element(new Pair(*(copyNode.element))), next(
                            copyNode.next) ,before(copyNode.before){
            }
            Node& operator++(){
                if(this->next){
                return(this->next);
                }
                Node* node=new Node;
                node->next=NULL;
                node->element=NULL;
                //node->first=NULL;
                return(node);
            }
            Node operator++(int n){
                Node result=*this;
                ++this;
                return result;
            }
            Node& operator--(){
                return(this->before);
            }
            bool operator==(const Node& node)const{
                if(node.before==this->before&&node.element==this->element&&node.next==this->next){
                    return true;
                }
                return false;
            }
            bool operator!=(const Node& node)const{
            if(this==node){
                return false;
            }
            return true;
            }
            Node& operator=(const Node& node){
                if(this==&node){
                    return *this;
                }
                this->before=node.before;
                this->element=node.element;
                this->next=node.next;
                return *this;
            }
            }
        };
    class iterator{
    public:
        Node* p;
        iterator():p(){}
        iterator(const iterator& it):p(it.p){}
        ~iterator(){
        }
        iterator& operator++(){
        this->p=this->p->next;
        if(this->p){
            return *this;
        }
        this->p=NULL;
        return *this;
        }
        iterator operator++(int n){
            iterator result=*this;
            ++*this;
            return result;
        }
        iterator& operator=(const iterator& it){
            if(this==&it){
                return *this;
            }
            this->p=it.p;
            return *this;
        }
        bool operator==(const iterator& iterator)const{
            if(this->p==iterator.p){
                return true;
            }
            return false;
        }
        bool operator!=(const iterator& iterator)const{
            if(this->p!=iterator.p){
                return true;
            }
            return false;
        }
        const Pair& operator*(){
            if(*this==end()){
                throw MapElementNotFoundException();
            }
            return *p->element;
        }
        iterator& begin(){
            while(p->before!=NULL){
                p--;
            }
            return *this;
        }
        iterator& end(){
            iterator* it=new iterator();
            it->p=NULL;
            return *it;
        }

    };
    int mapSize;
    Node* head;
    iterator iter;
    ValueType initializer;
    CompareFunction compareFunc;
    MtmMap(ValueType val):mapSize(0),head(),iter(),initializer(val){}

您需要使用一个准确反映目录结构的相对路径。示例:

include my_set/Makefile
include tests/Makefile
# ...
TEST_OBJS1 = tests/cache_test.o cache.o 
TEST_OBJS2 = tests/memcache_test.o memcache.o cache.o user.o
TEST_OBJS3 = my_set/my_set_test.o my_set/my_set.o
# ...

基本上,你会想在你"包括"的每个子目录中创建一个"Makefile",它定义了在这些子目录中构建每个目标的规则(并使用规则中相对于根Makefile目录的路径),在你的顶级Makefile中,类似地,您将通过它们相对于顶级Makefile目录的路径(而不是文件名)来引用这些文件/目标。

给定的目录布局

main/
    cache.c
    cache.h
    memcache.c
    memcache.h
    user.c
    user.h
    my_set/
        my_set.c
        my_set.h
        my_set_test.c
    tests/
        memcache_test.c
        cache_test.c

定义宏时,如果只评估一次,请使用:=而不是=

当为外部可执行文件提供宏名称时,始终提供完整路径

注意:答案在make rule命令的开头包含空格必须用选项卡替换才能实现makefile语法

该问题没有指定某些头文件的位置,也没有指示某些.c文件使用了哪些头文件。

假设所有没有具体位于目录布局中的头文件实际上都在"main/"目录中。

那些明确的编译规则是头文件依赖性未知的,具有以下文本??? which header files ???

提议的makefile内容如下:

#CC=gcc
CC := /usr/bin/gcc
RM := /usr/bin/rm
# you stated the executables were to be in the main directory
#EXEC1 = /tests/cache_test
#EXEC2 = /tests/memcache_test
#EXEC3 = /my_set/my_set_test
EXEC1 := cache_test
EXEC2 := memcache_test
EXEC3 := my_set_test
# when object in sub directory, include the directory path
TEST_OBJS1 := tests/cache_test.o cache.o
TEST_OBJS2 := tests/memcache_test.o memcache.o cache.o user.o
TEST_OBJS3 := my_set/my_set_test.o my_set/my_set.o
LIBS         := -L. -lmtm
DNDEBUG_FLAG := -DNDEBUG
CFLAGS := -Wall -Werror -pedantic-errors -std=c99 -g
# need to tell ''make'' that certain targets do not produce a output file
.PHONY : all clean
# first/default target in makefile
# it has dependencies on each of the 3 executables
#   so, by default, the three executables will be generated
all: $(EXEC1) $(EXEC2) $(EXEC3)
        @echo "all done"
# link rules needs any 'special' library path and library name parameters
$(EXEC2): $(TEST_OBJS2)
        $(CC) $(CFLAGS) $(DNDEBUG_FLAG) $(TEST_OBJS2) -o  $@  $(LIBS)
$(EXEC1): $(TEST_OBJS1)
        $(CC) $(CFLAGS) $(DNDEBUG_FLAG) $(TEST_OBJS1) -o  $@  $(LIBS)
$(EXEC3): $(TEST_OBJS3)
        $(CC) $(CFLAGS) $(DNDEBUG_FLAG) $(TEST_OBJS3) -o  $@  $(LIBS)

# compile rules have no use for library path and library name parameters
#     they do have use for where to find the user supplied header files
#     especially if those header files are not in the current directory
# only compile one file in each explicit compile rule
# always place the source file name as the first dependency parameter
#      so '$<' can be used to reference it
cache.o: cache.c cache.h list.h set.h
        $(CC) $(CFLAGS) $(DNDEBUG_FLAG) -c $< -o $@  -I.
user.o: user.c user.h set.h list.h
        $(CC) $(CFLAGS) $(DNDEBUG_FLAG) -c $< -o $@  -I.
memcache.o: memcache.c cache.h list.h set.h user.h memcache.h map.h
        $(CC) $(CFLAGS) $(DNDEBUG_FLAG) -c $< -o $@  -I.
my_set/my_set.o: my_set/my_set.c my_set/my_set.h list.h
        $(CC) $(CFLAGS) $(DNDEBUG_FLAG) -c $< -o $@  -I. -Imy_set/.
tests/cache_test.o : tests/cache_test.c ??? which header files ???
        $(CC) $(CFLAGS) $(DNDEBUG_FLAG) -c $< -o $@  -I. -Itests/.
tests/memcache_test.o : tests/memcache_test.c ??? which header files ???
        $(CC) $(CFLAGS) $(DNDEBUG_FLAG) -c $< -o $@  -I. -Itests/.
my_set/my_set_test.o : my_set/my_set_test.c ??? which header files ???
        $(CC) $(CFLAGS) $(DNDEBUG_FLAG) -c $< -o $@  -I. -Imy_set/.

# all three directories need to be cleaned
# the ( cd .... && .... ) lines
# start a new sub shell, 
# cd to the appropriate directory, 
# perform the rm function, 
# and exit the sub shell
# which results in being back in the original directory
clean:
        $(RM) -f *.o $(EXEC1) $(EXEC2) #(EXEC3)
        ( cd my_set && $(RM) -f *.o )
        ( cd tests  && $(RM) -f *.o )