分享
 
 
 

打造自己的Iterator Adapter – File Iterator

王朝other·作者佚名  2006-01-31
窄屏简体版  字體: |||超大  

介绍

有时,我们需要列出Windows中某个文件夹里的文件。按照常规的方法就是把搜索到的文件在某个链表的尾端插入。例如下面的伪码所示

void list_file(const std::string& path, file_list& flist)

{

while(判断path是否有文件)

{

filst.push_back(文件名);

查找下一个文件;

}

}

list_file这个算法虽然能正常工作,但是它和数据容器file_list紧密相联,形成极高的耦合。如果,现在又要搜索某个文件夹里的指定文件,那么就需要用list_file进行全盘搜索或重新再设计一个算法。

对于这样的设计,我们先回归到STL上,STL为我们展示了C++程序设计的新思维。迭代器(Iterators)是一种抽象概念,这种模式提供了一种方法来遍历某个聚合物中的所有元素,而又不暴露出聚合物内部的细节。而STL中的迭代器,将数据容器和算法分开,而达到算法的泛化。更重要的是,它提供了一个统一的形式来让程序对不同数据结构进行操作。

那么我们可以将一个文件夹抽象为数据容器,而里面的子文件夹和文件则抽象为元素。这样一来,就可以设计一种迭代器来遍历文件夹中的文件,如果设计一个符合STL规范的迭代器,还可以与STL算法完美结合,从而利用STL提供的算法。

设计目标

看看下面的代码

void display(const file_info& x)

{

std::cout<<(x.is_dir()?" * ":" "); //输出* 表示这是一个文件夹

std::cout<<x.file_name()<<std::endl;

}

int main()

{

std::for_each(file_iterator<file_info>("c:\\windows"), file_iterator<file_info>(), display);

}

我们还可以将文件列表放到一个容器中

std::vector<file_info> vec_file;

std::copy(file_iterator<file_info>("c:\\windows"), file_iterator<file_info>(), std::back_inserter(vec_file));

对于设计符合STL规范的迭代器,我们有必要来回忆一下关于STL中迭代器的概念。

Let’s get started

事实上,迭代器并不是一个单纯的概念,STL根据不同的操作情况,将迭代器分为五类:

Input Iterator 只能单步向前迭代元素,不允许修改由该类迭代器引用的元素。

Output Iterator 该类迭代器和Input Iterator极其相似,也只能单步向前迭代元素,不同的是该类迭代器对元素只有写的权力。

Forward Iterator 该类迭代器可以在一个正确的区间中进行读写操作,它拥有Input Iterator的所有特性,和Output Iterator的部分特性,以及单步向前迭代元素的能力。

Bidirectional Iterator 该类迭代器是在Forward Iterator的基础上提供了单步向后迭代元素的能力。

Random Access Iterator 该类迭代器能完成上面所有迭代器的工作,它自己独有的特性就是可以像指针那样进行算术计算,而不是仅仅只有单步向前或向后迭代。iter + n,就表示以常量时间向后移动到n个单位。

这五类迭代器的从属关系,如下图所示,其中箭头A→B表示,A是B的强化类型,这也说明了如果一个算法要求B,那么A也可以应用于其中。

开始设计

先从上面五类迭代器中选出一种来说明我们即将设计的iterator,结合实际情况,我们的目的是获得某个目录下的文件,那么Input Iterator就是我们所选择的。

一个Input Iterator的要求是:

1、 可默认构造的:提供默认构造函数

2、 可复制的:提供拷贝构造函数 和 拷贝赋值操作符

3、 可比较的:提供operator==,和operator!=

4、 单步向前迭代能力:提供前置自增运算符 和 后置自增运算符

5、 解引用能力:提供operator*

如何构造一个指向尾端的迭代器?可以借用IStream Iterators的思想,默认构造一个该迭代器的对象,就表示指向尾端的迭代器。

如果在文件迭代过程中由Windows引发了错误,如何处理呢?对于将要设计的文件迭代器来说,并没有写入操作,随时可以退出迭代,而Windows引发的错误却有必要传递给调用者,但是在这里用传统的返回值传递是不可能的,因为iterator是一个对象,而不是函数的调用,因此,我们可以考虑用异常。抛出一个file_exception的异常对象。

关于文件迭代器的value_type。由于文件的信息是由Windows提供,而Windows将文件的信息都在一个WIN32_FIND_DATA结构对象中。为了把文件信息传递给value_type的对象,那么就有必要为value_type提供一个T(const WIN32_FIND_DATA&)的构造函数。

资源问题,对文件的搜索会使用Windows返回的句柄,这个系统对象的句柄并不像C++中内存资源那样可以进行拷贝构造,而即将设计的文件迭代器是可以被拷贝构造的,这就意味着多个文件迭代器会引用同一个系统对象,这样一来,当文件迭代器发生析构时,就无法正确地判断是否要释放当前的系统对象,如何解决这个问题呢?我们可以在文件迭代器中设计一个局部的类 file_operate_raii,通过RAII技巧,并加入引用记数就可以顺利解决这个问题。

线程安全问题,由于上面的提到的file_operate_raii其实就是一个被多个文件迭代器共读写的对象,那么我们就有必要考虑到线程安全的问题。

代码就是设计,下面来看看实现的C++代码

//file: file_iterator.h

#ifndef _FILE_ITERATOR_H

#define _FILE_ITERATOR_H

#include<windows.h>

#include<string>

#include<exception>

namespace f_iter

{

class file_exception

:public std::exception

{

public:

const char* what() const throw()

{

return "An error was occurred";

}

};

template<typename _Value_Type>

class file_iterator

:public std::iterator<std::input_iterator_tag, _Value_Type>

{

public:

file_iterator(const std::string& file_path)

:end_(false), file_path_(file_path), hfind_(0), p_fo_raii_(0)

{

_m_prepare();

}

file_iterator():end_(true), hfind_(0),p_fo_raii_(0){} //默认构造

file_iterator(const file_iterator& x) //可拷贝

:end_(x.end_), file_path_(x.file_path_), hfind_(x.hfind_), p_fo_raii_(x.p_fo_raii_), value_(x.value_)

{

if(p_fo_raii_)

p_fo_raii_->increase();

}

file_iterator& operator=(const file_iterator& x) //可拷贝

{

if(this == &x) return *this; //防止自我拷贝

end_ = x.end_;

file_path_ = x.file_path_;

hfind_ = x.hfind_;

value_ = x.value_;

if(p_fo_raii_)

{

p_fo_raii_->decrease();

if(p_fo_raii_->empty())

{

delete p_fo_raii_;

}

}

p_fo_raii_ = x.p_fo_raii_;

if(p_fo_raii_)

p_fo_raii_ ->increase();

return *this

}

~file_iterator()

{

if(p_fo_raii_)

{

p_fo_raii_->decrease();

if(p_fo_raii_->empty())

{

delete p_fo_raii_;

p_fo_raii_ = 0;

}

}

}

public:

const _Value_Type& //对元素只读,所以返回const

operator*() const { return value_; }

const _Value_Type*

operator->() const { return &(operator*()); }

file_iterator& //单步向前迭代的能力

operator++()

{ _m_read(); return *this; }

file_iterator

operator++(int)

{

file_iterator tmp = *this;

_m_read();

return tmp;

}

bool equal(const file_iterator& x) const

{

if(end_ && (end_ == x.end_)) return true;

return (strcmp(find_file_data_.cFileName, x.find_file_data_.cFileName) == 0

&&

file_path_ == x.file_path_);

}

private:

void _m_prepare()

{

if(p_fo_raii_ == 0)

p_fo_raii_ = new file_operate_raii(file_path_, find_file_data_);

hfind_ = p_fo_raii_->get_handle();

if(hfind_ == INVALID_HANDLE_VALUE)

{

end_ = true;

hfind_ = 0;

return;

}

if(*find_file_data_.cFileName=='.')

{

::FindNextFile(hfind_, &find_file_data_);

if(::FindNextFile(hfind_, &find_file_data_) !=0 )

value_ = _Value_Type(find_file_data_); //对_Value_Type的要求有T(const WIN32_FIND_DATA&)的构造函数

else

{

end_ = true;

hfind_ = 0;

if(ERROR_NO_MORE_FILES != ::GetLastError())

throw file_exception();

}

}

else

{

value_ = _Value_Type(find_file_data_);

}

}

void _m_read()

{

if(FindNextFile(hfind_, &find_file_data_) !=0 )

{

value_ = _Value_Type(find_file_data_); //对_Value_Type的要求有T(const WIN32_FIND_DATA&)的构造函数

}

else

{

end_ = true;

hfind_ = 0;

if(ERROR_NO_MORE_FILES != ::GetLastError())

throw file_exception();

}

}

private:

//class lock解决线程安全问题

class lock{

public:

lock(){

::InitializeCriticalSection(&cs_);

}

~lock(){

::DeleteCriticalSection(&cs_);

}

void enter() const{

::EnterCriticalSection(&cs_);

}

void leave() const{

::LeaveCriticalSection(&cs_);

}

private:

mutable ::CRITICAL_SECTION cs_;

};

//class file_operate_raii 负责系统对象的创建和释放

class file_operate_raii

{

file_operate_raii(); //non-default-constructable

file_operate_raii(const file_operate_raii&); //non-copyable

file_operate_raii& operator=(const file_operate_raii&); //non-copyable

public:

file_operate_raii(const std::string& x, WIN32_FIND_DATA& y)

:counter_(1)

{

handle_ = ::FindFirstFile((x + "\\*").c_str(), &y);

}

~file_operate_raii()

{

::FindClose(handle_);

}

public:

void increase()

{

lock_.enter();

counter_++;

lock_.leave();

}

void decrease()

{

lock_.enter();

counter_--;

lock_.leave();

}

bool empty() const

{ return counter_ == 0; }

size_t count() const

{ return counter_; }

::HANDLE get_handle() const

{ return handle_; }

private:

::HANDLE handle_;

size_t counter_;

};

private:

bool end_;

_Value_Type value_;

std::string file_path_;

WIN32_FIND_DATA find_file_data_;

::HANDLE hfind_;

file_operate_raii* p_fo_raii_;

};

template<typename _Value_Type>

inline bool operator==(const file_iterator<_Value_Type> & x, const file_iterator<_Value_Type> & y)

{

return x.equal(y);

}

template<typename _Value_Type>

inline bool operator!=(const file_iterator<_Value_Type> & x, const file_iterator<_Value_Type> & y)

{

return !x.equal(y);

}

}

#endif

上面一大堆代码,您也许看出一点东西来了。因为多个file_iterator对象可以引用同一个系统对象,换句话说就是,比如两个file_iterator对象a,b

a = b;

if(++a == ++b){} //这个判断不会被成立

是的,Input Iterator有这个特性!

最后,我们来使用这个file_iterator

#include"file_iterator.h"

#include<algorithm>

#include<iostream>

using namespace std;

class file_info

{

public:

file_info() :is_dir_(false)

{}

file_info(const WIN32_FIND_DATA& x) //file_iterator要求这个构造函数

:is_dir_((FILE_ATTRIBUTE_DIRECTORY & x.dwFileAttributes) == FILE_ATTRIBUTE_DIRECTORY), file_name_(x.cFileName)

{}

public:

std::string file_name() const

{ return file_name_; }

bool is_dir() const

{ return is_dir_; }

private:

bool is_dir_;

std::string file_name_;

};

void display(const file_info& x)

{

std::cout<<(x.is_dir()?" * ":" ");

std::cout<<x.file_name()<< std::endl;

}

int main()

{

using namespace std;

using namespace f_iter;

for_each(file_iterator<file_info>("c:\\windows"), file_iterator<file_info>(), display);

}

对于这个设计可能存在BUG,欢迎各位看官指出来,同时如果您有什么想法或意见,我希望各位通过电子邮件来开导我! 非常感谢 boxban指出文章中的错误和提出意见。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有