panda ID:pandaxcl
81479次访问,排名1161好友0人,关注者3
pandaxcl的文章
原创 61 篇
翻译 0 篇
转载 0 篇
评论 154 篇
pandaxcl的公告

博客文档资源下载在本人的网站下载!!!

我也优先在我的网站论坛上面回答问题

在研究C++自动化编程好久之后,发现C++自动化编程在国内还是一个空白。所谓的C++自动化编程,简单点说就是采用了C++的高级模板技术配合产生式编程技法实现了C++代码的自我配置,自动维护代码之间的种种一致性问题。关于这个问题的讨论,将会在我的网站上面进行细致的讨论。如果有问题,欢迎来我的网站提问哦。看看下面的我的网站的链接。

EMail:pandaxcl@163.com

QQ:56637059

我的网站: http://www.autodev.net

最近评论
skyapples:public std::list<command*> // 用来记录子命令的命令队列
为什么不把它放在batch里面,而非要来个多重继承,不知道楼主是怎么想的?
skyapples:不得不说,这真是一篇好文章,作者是用心做事情的人
skyapples:不得不说,这真是一篇好文章,作者是用心做事情的人
huxi043715:博主,在很强阿。你的文章也很容易懂。
wangwei200508:呵呵,谢了
您的这里指到自己硬盘了
<a href="file:///D:/work/lex_yacc/chapter01/lexyacc.rar.png" target="_top">这里</a>
文章分类
收藏
    相册
    友情连接
    小熊猫
    我的另外一个博客
    我的网站-自动化编程社区
    我的论坛-自动化编程社区论坛
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 C++中使用STL轻松实现序列化收藏

    新一篇: 撤销和重做(Undo和Redo)的C++完美实现(3) | 旧一篇: C++类什么情况下需要虚拟析构函数

    #if 0

    在用C++编写应用程序的过程中,经常涉及到序列化的问题,但是序列化的问题通常都
    会有非常繁琐的过程代码需要书写,本文中就是通过简单的步骤实现了程序的序列化问题
    ,简单直接,和其它的序列化方案有着很大的不同。

    首先来看看简单的数据写入文件和从文件读入数据的代码:

    特别注解:本人特别喜欢用STL来书写代码,一方面是便于移植,但是另一方却是在于
    用STL书写的代码简单直接,可读性好。如果还不熟悉STL,本文则不大适合你:)

    #endif
    #if CODE1
    ////////////////////////////////////////////////////////////////////////////////
    //模拟程序序列化的简单代码
    #include <iostream>//cout
    #include <fstream>//ofstream,ifstream
    #include <vector>//vector
    #include <iterator>//ostream_iterator,istream_iterator,back_inserter
    #include <numeric>//partial_sum
    #include <algorithm>//copy
    #include <string>
    #include <sstream>
    using namespace std;//简化代码的书写,经std名字空间成为默认名字空间
    int main()
    {
    {//从程序序列化到文件
    vector<int> v(5,1);//[1,1,1,1,1]
    partial_sum(v.begin(),v.end(),v.begin());//[1,2,3,4,5]
    ofstream out("data.txt");//生成文件输出流
    //将数组v中的数据全部输出到文件流中,这种操作在C++中成为文件操作
    //在这里暂时称为序列化到文件操作。实际上这里为了简单序列化的格式
    //为文本文件格式。如果需要其它的格式完全可以通过自定义输出流游标
    //的方式,或者重载运算符operator<<和operator>>实现不同的序列化格
    //式。可以参见本人的其它相关文档。
    copy(v.begin(),v.end(),ostream_iterator<int>(out," "));
    }
    {//从文件序列化到程序
    vector<int> v;//模拟应用程序中数据
    ifstream in("data.txt");//建立输入流
    //下面的这行代码从文件中提取数据到v中,模拟了应用程序的序列化过程
    copy(istream_iterator<int>(in),istream_iterator<int>(),back_inserter(v));
    //下面的这行代码仅仅只是为了显示是否真的被序列化到了程序中
    copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));
    }
    return 0;
    }
    ////////////////////////////////////////////////////////////////////////////////
    //该程序的输出如下:
    /*******************************************************************************
    1 2 3 4 5
    *******************************************************************************/
    //data.txt中的内容如下:
    /*******************************************************************************
    1 2 3 4 5
    *******************************************************************************/
    ////////////////////////////////////////////////////////////////////////////////
    #endif//CODE1
    #if 0
    很明显我们在应用程序中保存了数据的类型信息,如:文件中的[1,2,3,4,5]的类型“
    int”信息是保存在程序中的,这一点是和其它的序列化方案有着极大的不同,用过MFC的
    都知道,MFC中为了实现序列化功能,必须将所有的类型信息和数据信息同时保存在文件中
    ,虽然都是以二进制格式的形式,但是通过二进制编辑器可以很容易的看出你面保存的类
    型信息字符串,这一点是有安全隐患的。所以从安全和保密上讲,本文中的方式是MFC序列
    化机制所不能够做到的。从代码简洁程度和序列化文件的格式简单性来讲,本文中的方案
    也绝对不是MFC的序列化所能够轻易做到的。在考察Boost的serialize库的时候,发现文件
    中也保存了类型信息。
    讨论到了这里,你也许会说,这里只有一种类型的数据,如果类型多了,还能够用这
    种方式实现序列化么。可以非常肯定的告诉你,我已经成功的使用过这种方式实现了序列
    化了。下面来看一个非常直接的例子:
    #endif
    #if CODE2
    ////////////////////////////////////////////////////////////////////////////////
    //模拟程序序列化的简单代码
    #include <iostream>//cout
    #include <fstream>//ofstream,ifstream
    #include <sstream>//istringstream
    #include <string>//string
    #include <vector>//vector
    #include <iterator>//ostream_iterator,istream_iterator,back_inserter
    #include <numeric>//partial_sum
    #include <algorithm>//copy
    using namespace std;
    const char delimiter = '*';//用来分隔多种数据类型的分隔符
    int main()
    {
    {//从程序序列化到文件
    //v1,v2,v3,v4,v5用来模拟应用程序中的许多类型的数据,当然类型信息可以
    //是任意的类型,当然需要为每一种类型书写各自的operator<<和operator>>
    //运算符啦!具体的情况具体分析,熟悉STL的人自然可以根据本文中的各个
    //STL算法和容器的要求写出必须的函数和操作符了。
    vector<int> v1(3,1);//3个1
    vector<short> v2(4,2);//4个2
    vector<long> v3(5,3L);//5个3
    vector<float> v4(6,4.1);//6个4.1
    vector<double> v5(7,5.2);//7个5.2
    //一下的代码用来产生不同的类型序列
    partial_sum(v1.begin(),v1.end(),v1.begin());//[1,2,3]
    partial_sum(v2.begin(),v2.end(),v2.begin());//[2,4,6,8]
    partial_sum(v3.begin(),v3.end(),v3.begin());//[3L,6L,9L,12L,15L]
    partial_sum(v4.begin(),v4.end(),v4.begin());//[4.1,8.2,12.3,16.4,20.5,24.6]
    partial_sum(v5.begin(),v5.end(),v5.begin());//[5.2,10.4,15.6,20.8,26.0,31.2,36.4]
    ofstream out("data.txt");//生成文件输出流
    //将数组v中的数据全部输出到文件流中,这种操作在C++中成为文件操作
    //在这里暂时称为序列化到文件操作。实际上这里为了简单序列化的格式
    //为文本文件格式。如果需要其它的格式完全可以通过自定义输出流游标
    //的方式,或者重载运算符operator<<和operator>>实现不同的序列化格
    //式。可以参见本人的其它相关文档。
    //向文件中依次序列化所有的应用程序数据
    copy(v1.begin(),v1.end(),ostream_iterator<int >(out," "));
    out << delimiter << endl;//输出分隔符分开不同的数据类型
    copy(v2.begin(),v2.end(),ostream_iterator<short >(out," "));
    out << delimiter << endl;//输出分隔符分开不同的数据类型
    copy(v3.begin(),v3.end(),ostream_iterator<long >(out," "));
    out << delimiter << endl;//输出分隔符分开不同的数据类型
    copy(v4.begin(),v4.end(),ostream_iterator<float >(out," "));
    out << delimiter << endl;//输出分隔符分开不同的数据类型
    copy(v5.begin(),v5.end(),ostream_iterator<double>(out," "));
    }
    {//从文件序列化到程序
    //v1,v2,v3,v4,v5用来模拟应用程序中的许多类型的数据
    vector<int> v1;
    vector<short> v2;
    vector<long> v3;
    vector<float> v4;
    vector<double> v5;
    ifstream in("data.txt");//建立文件输入流
    //从文件中依次提取出序列化到文件的所有的数据
    copy(istream_iterator<int >(in),istream_iterator<int >(),back_inserter(v1));
    in.clear();in.ignore(10,delimiter);//让流恢复状态继续提取数据
    copy(istream_iterator<short >(in),istream_iterator<short >(),back_inserter(v2));
    in.clear();in.ignore(10,delimiter);//让流恢复状态继续提取数据
    copy(istream_iterator<long >(in),istream_iterator<long >(),back_inserter(v3));
    in.clear();in.ignore(10,delimiter);//让流恢复状态继续提取数据
    copy(istream_iterator<float >(in),istream_iterator<float >(),back_inserter(v4));
    in.clear();in.ignore(10,delimiter);//让流恢复状态继续提取数据
    copy(istream_iterator<double>(in),istream_iterator<double>(),back_inserter(v5));
    //下面的5行代码仅仅只是为了显示是否真的被序列化到了程序中
    copy(v1.begin(),v1.end(),ostream_iterator<int >(cout," "));cout<<endl;
    copy(v2.begin(),v2.end(),ostream_iterator<short >(cout," "));cout<<endl;
    copy(v3.begin(),v3.end(),ostream_iterator<long >(cout," "));cout<<endl;
    copy(v4.begin(),v4.end(),ostream_iterator<float >(cout," "));cout<<endl;
    copy(v5.begin(),v5.end(),ostream_iterator<double>(cout," "));cout<<endl;
    }
    return 0;
    }
    ////////////////////////////////////////////////////////////////////////////////
    //该程序的输出如下:
    /*******************************************************************************
    1 2 3
    2 4 6 8
    3 6 9 12 15
    4.1 8.2 12.3 16.4 20.5 24.6
    5.2 10.4 15.6 20.8 26 31.2 36.4
    *******************************************************************************/
    //data.txt中的内容如下:
    /*******************************************************************************
    1 2 3 *
    2 4 6 8 *
    3 6 9 12 15 *
    4.1 8.2 12.3 16.4 20.5 24.6 *
    5.2 10.4 15.6 20.8 26 31.2 36.4
    *******************************************************************************/
    ////////////////////////////////////////////////////////////////////////////////
    #endif//CODE2
    #if 0

    很明显,CODE2中的代码段已经满足了我们的序列化要求。已经可以序列化各种不同类型
    的数据,虽然CODE2中使用的是基本类型中的5种情况,但是由于C++的特色,对于类这样的扩
    展类型完全可以象基本类型一样来对待,至于怎样才能够象基本类型一样来对待,这是C++基
    础知识,基本上就是重载C++运算符。

    有人或许会说:上面的方法并不能够处理指针容器(容器中放置的是指针而不是对象)
    实际上这是一种误导,虽然上面的代码中看不到指针的东西,但是STL中确确实实是采用的动
    态内存分配的方式实现对象管理的,我们在使用STL的过程中实际上已经让STL替我们管理了
    麻烦不断的内存指针问题!更确切点说,STL的游标概念就是泛化的指针。尽管如此,我们还
    是要避免在容器中使用指针,虽然STL允许这么做,但是我并不提倡这一点,因为不用指针已
    经可以很好的实现自己想要实现的任何功能了(当然这仅仅只是限于本文所介绍的序列化方
    案)。对于其它的C++功能方面指针还是有它独到的能力,就我的使用经验来说,不用指针完
    全可以实现指针所能够实现的所有功能,当然这里面包含了许许多多的C++高级知识,能够用
    指针方便实现的当然还是用指针实现的好啊:)

    在这里作为提示,给出如果非要使用指针容器的情况如何实现序列化的方法:对指针容
    器进行特殊处理,序列化到文件的时候一定要保证并不是把指针所指的对象序列化到了文件
    而是将该指针在原来的对象容器中的索引保存到文件,从文件提取数据的时候同样是根据索
    引值到对象容器中查找到对象并得到相应的指针放到指针容器中。从本段前面的讨论可以知
    道:如果存在某个指针容器,就一定存在对应的对象容器,并且,在序列化输出和序列化输
    入的时候都是先序列化的对象容器,然后才是指针容器,下面的CODE3中给出了具体的实例
    代码:

    #endif
    #ifdef CODE3
    ////////////////////////////////////////////////////////////////////////////////
    //模拟程序序列化的简单代码
    #include <iostream>//cout
    #include <fstream>//ofstream,ifstream
    #include <vector>//vector
    #include <iterator>//ostream_iterator,istream_iterator,back_inserter
    #include <numeric>//partial_sum
    #include <algorithm>//copy
    #include <string>
    #include <sstream>
    #include <functional>
    #include <map>
    using namespace std;
    //下面是针对输入和输出专门写的序列化模板
    template<class Container>
    ofstream& operator << (ofstream&out,Container&c)
    {
    typedef typename Container::value_type T;
    copy(c.begin(),c.end(),ostream_iterator<T>(out," "));
    out << endl;
    return out;
    }
    template<class Container>
    ifstream& operator >> (ifstream&in,Container&c)
    {
    typedef typename Container::value_type T;
    string buffer;//每一行数据的临时缓冲区
    getline(in,buffer);//从文件中读取一行数据到缓冲区中
    istringstream isin(buffer);//构造输入字符串流
    copy(istream_iterator<T>(isin),istream_iterator<T>(),back_inserter(c));
    return in;
    }
    template<class Container,class PointerContainer>
    ofstream& operator << (ofstream&out,pair<Container*,PointerContainer*> p)
    {
    Container&c = *(p.first);
    PointerContainer&cp = *(p.second);
    typename PointerContainer::iterator it = cp.begin();
    for(;it!=cp.end();++it)
    {
    typename Container::iterator itc;
    for(;itc!=c.end();++itc){
    if(&(*itc)==*it) out << distance(c.begin(),itc) << " " ;
    }
    }
    out << endl;
    return out;
    }
    template<class Container,class PointerContainer>
    ifstream& operator >> (ifstream&in,pair<Container*,PointerContainer*> p)
    {
    Container&c = *(p.first);
    PointerContainer&cp = *(p.second);
    typedef typename Container::value_type T;
    string buffer;//每一行数据的临时缓冲区
    getline(in,buffer);//从文件中读取一行数据到缓冲区中
    istringstream isin(buffer);//构造输入字符串流
    size_t tmp;//用来临时保存读取的索引号
    while(isin >> tmp)//读取成功isin状态表示成功
    {
    typename Container::iterator it = c.begin();
    advance(it,tmp);//将游标移动到指定的索引位置
    cp.push_back(&(*it));//将这个对象的地址保存到地址数组中
    }
    return in;
    }

    int main()
    {
    {//从程序序列化到文件
    vector<int> v(5,1);//[1,1,1,1,1]
    vector<int*> vp;//指针容器
    vp.push_back(&v[1]);
    vp.push_back(&v[3]);
    partial_sum(v.begin(),v.end(),v.begin());//[1,2,3,4,5]
    ofstream out("data.txt");//生成文件输出流
    //将数组v中的数据全部输出到文件流中,这种操作在C++中成为文件操作
    //在这里暂时称为序列化到文件操作。实际上这里为了简单序列化的格式
    //为文本文件格式。如果需要其它的格式完全可以通过自定义输出流游标
    //的方式,或者重载运算符operator<<和operator>>实现不同的序列化格
    //式。可以参见本人的其它相关文档。
    out << v << make_pair(&v,&vp) ;
    }
    {//从文件序列化到程序
    vector<int> v;//模拟应用程序中数据
    vector<int*> vp;//指针容器
    ifstream in("data.txt");//建立输入流
    //下面的这行代码从文件中提取数据到v中,模拟了应用程序的序列化过程
    in >> v >> make_pair(&v,&vp) ;
    //下面的这行代码仅仅只是为了显示是否真的被序列化到了程序中
    copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));
    cout << endl;
    //输出指针数组中指向的对象数据
    struct X{static void print(const int*ptr){cout<<*ptr<<" ";}};
    for_each(vp.begin(),vp.end(),X::print);
    }
    return 0;
    }
    ////////////////////////////////////////////////////////////////////////////////
    //该程序的输出如下:
    /*******************************************************************************
    1 2 3 4 5
    2 4
    *******************************************************************************/
    //data.txt中的内容如下:
    /*******************************************************************************
    1 2 3 4 5
    1 3
    *******************************************************************************/
    ////////////////////////////////////////////////////////////////////////////////
    #endif//CODE3

    发表于 @ 2006年04月03日 21:59:00|评论(loading...)|编辑

    新一篇: 撤销和重做(Undo和Redo)的C++完美实现(3) | 旧一篇: C++类什么情况下需要虚拟析构函数

    评论

    #Kyle 发表于2006-04-15 19:58:00  IP: 219.133.143.*
    对于是否在序列化文件中保存对象类型,我认为保存是正确的。否则使用其他程序读取无类型信息的序列化文件,是对新程序开发者的挑战。xml的广泛使用也是这个原因吧。当然,不保存类型信息,使用程序中序列化的顺序耦合关系保证,可能性能上会好,安全方面未必有提高哦。窃以为,信息安全性是可以使用专门的层次完成的,不必在序列化的层次考虑过多。
    #pandaxcl 发表于2006-04-15 22:39:00  IP: 202.114.78.*
    谢谢评论,这里是一个例子,是我的一点看法,实际上很容易改写称保存类型信息的格式,不过我写这篇文档的目的是为了撤销和重做机制的序列化作准备的啊,可以看看!
    #lostown 发表于2006-04-18 23:43:00  IP: 222.67.61.*
    不错的文章,正在看...
    #许帆 发表于2006-05-17 21:34:00  IP: 218.7.32.*
    对我有难度,看不太懂,我还是去学学更基础的吧
    #mslk 发表于2006-05-25 12:41:00  IP: 222.90.193.*
    很不错,不过不能叫做序列化,只能叫读写数据文件

    “如果存在某个指针容器,就一定存在对应的对象容器”,这个假定不成立!
    序列化要能处理程序运行中heap分配的对象,这是不会存在对象容器的。
    最最主要的,难到不让多态序列化,对象容器会造成对象的切割(slice)。
    #mslk 发表于2006-05-25 12:51:00  IP: 222.90.193.*
    对于容器,元素个数还是先存一下较好,很难保证元素和分隔符一定不重复,再多加一个std::vector<std::string>元素有"\n","*"
    #alai04 发表于2006-08-23 12:07:00  IP: 210.21.12.*
    这种方法处理string会很麻烦,不过作为示例是可以的,如果真的要用,我还是会选择boost的serialize
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © pandaxcl