免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1656 | 回复: 3
打印 上一主题 下一主题

[C++] C++ 常用模板武道会 第一场 上 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2003-03-25 20:14 |只看该作者 |倒序浏览
++ 常用模板武道会 第一场 上  
vector v.s. list v.s. deque
原创作者:beyond_ml

Ladies & Gentlemem:

大家好,这里是首届C++模板武道会的现场,本次武道会由beyond_ml做东,第一场解说员为beyond_ml。由于首次举办这样规模空前的盛会,难免有疏漏之处,还请各位高手不吝赐教。Beyond_ml有理啦。同时也欢迎各位大虾把此次武道会看做是一个虚基类,不断继承,派生出新的比赛。

比赛开始:

首先介绍比武参赛者:

Vector:金山词霸翻译成:矢量,向量等,C++容器模板中的大哥大,就像是一个加强版的队列,之所以这样说,是因为它不但有队列形式的索引,还能动态的添加扩充。使用尤其广泛,并且在许多经典教材中被作为重点介绍。

特点:把被包含的对象以数组的形式存储,支持索引形式的访问(这种访问速度奇快无比)。但由此也产生了一个问题,由于数据存储形式的固定化,你如果想在他中间部位insert对象的话,搞不好会让你吃尽苦头。因为他在分配空间的时候,可是成块分配的连续空间哦。

Deque:英文“double-ended-queue”。名如其人,这是C++有序容器中闻名遐迩的双向队列。他在设计之初,就为从两端添加和删除元素做了特殊的优化。同样也支持随即访问,也有类似于vector的[ ]操作符,但大家不要因此就把他和vector混为一潭哦。

特点:从本质上讲,他在分配内存的时候,使用了MAP的结构和方法。化整为零,分配了许多小的连续空间,因此,从deque两端添加、删除元素是十分方便的。最重要的一点:如果在不知道内存具体需求的时候,使用deque绝对是比vector好的,具体怎么好,比武为证。另外在插一句,不知是不是有意设计,大多数情况下,deque和vector是可以互换使用的。

List:模板中的双向链表。设计他的目的可能就是为了在容器中间插入、删除吧,所以有得比有失,他的随机访问速度可不敢恭维。而且没有[ ]操作。即便你是为了从前到后的顺序访问,也不见得就能快多少,“爱用不用,反正俺有强项!”List还挺哼,这也难怪,看看他的特点吧。

特点:随机的插入、删除元素,在速度上占有明显的优势。并且,由于内存分配不连续,他对插入的要求也十分的低。所以在使用大对象的时候,这可是一个不错的选择。

“闲言碎语不要讲,开打,开打。”

“咚……”

比武正式开始:

第一局:比一比谁的内存管理强。

比试方法:人为引起存储的对象数据溢出,分别看看系统消耗。系统消耗在这里指:对象构造函数、拷贝函数、析构函数的调用次数。

测试程序如下:

noisy.h  …… 包含了测试对象,每次在调用构造、拷贝、析构函数的时候,都会打印相应的提示。

  1. //: C04:Noisy.h

  2. // A class to track various object activities

  3. #ifndef NOISY_H

  4. #define NOISY_H

  5. #include <iostream>;

  6. class Noisy

  7. {

  8.        static long create, assign, copycons, destroy;

  9.        long id;

  10.        public:

  11.        Noisy() : id(create++)

  12.        {

  13.               std::cout << "d[" << id << "]";

  14.        }

  15.        Noisy(const Noisy& rv) : id(rv.id)

  16.        {

  17.               std::cout << "c[" << id << "]";

  18.               copycons++;

  19.        }

  20.        Noisy& operator=(const Noisy& rv)

  21.        {

  22.               std::cout << "(" << id << ")=[" <<

  23.               rv.id << "]";

  24.               id = rv.id;

  25.               assign++;

  26.               return *this;

  27.        }

  28.        friend bool

  29.        operator<(const Noisy& lv, const Noisy& rv)

  30.        {

  31.               return lv.id < rv.id;

  32.        }

  33.        friend bool

  34.        operator==(const Noisy& lv, const Noisy& rv)

  35.        {

  36.               return lv.id == rv.id;

  37.        }

  38.        ~Noisy()

  39.        {

  40.               std::cout << "~[" << id << "]";

  41.               destroy++;

  42.        }

  43.        friend std::ostream&

  44.        operator<<(std::ostream& os, const Noisy& n)

  45.        {

  46.               return os << n.id;

  47.        }

  48.        friend class NoisyReport;

  49. };



  50. struct NoisyGen

  51. {

  52.        Noisy operator()() { return Noisy(); }

  53. };

  54. // A singleton. Will automatically report the

  55. // statistics as the program terminates:

  56. class NoisyReport

  57. {

  58.        static NoisyReport nr;

  59.        NoisyReport() {} // Private constructor

  60.        public:

  61.        ~NoisyReport()

  62.        {

  63.               std::cout << "\n-------------------\n"

  64.               << "Noisy creations: " << Noisy::create

  65.               << "\nCopy-Constructions: "

  66.               << Noisy::copycons

  67.               << "\nAssignments: " << Noisy::assign

  68.               << "\nDestructions: " << Noisy::destroy

  69.               << std::endl;

  70.        }

  71. };

  72. // Because of these this file can only be used

  73. // in simple test situations. Move them to a

  74. // .cpp file for more complex programs:

  75. long Noisy::create = 0, Noisy::assign = 0,

  76. Noisy::copycons = 0, Noisy::destroy = 0;

  77. NoisyReport NoisyReport::nr;

  78. #endif // NOISY_H ///:~
复制代码



目标:插入一千个Noisy对象。

Vector上场

  1. //: C04:VectorOverflow.cpp

  2. // Shows the copy-construction and destruction

  3. // That occurs when a vector must reallocate

  4. // (It maintains a linear array of elements)

  5. #include "noisy.h"

  6. #include <vector>;

  7. #include <iostream>;

  8. #include <string>;

  9. #include <cstdlib>;

  10. using namespace std;

  11. int main(int argc, char* argv[])

  12. {

  13.        int size = 1000;

  14.       if(argc >;= 2) size = atoi(argv[1]);

  15.        vector<Noisy>; vn;

  16.        Noisy n;

  17.        for(int i = 0; i < size; i++)

  18.        vn.push_back(n);

  19.        cout << "\n cleaning up \n";

  20. } ///:~
复制代码


测试结果:

  1. Noisy creations: 1                     (构造函数调用)

  2. Copy-Constructions: 2023                     (拷贝函数调用)

  3. Assignments: 0                                   (赋值调用)

  4. Destructions: 2024                                   (析构调用)
复制代码

Beyond_ml评论:哇!老大,我只是插一千个对象而已,你怎么一下子调2023次拷贝函数,相应的还有2024次析构调用,哎,看来,如果你插入的数据超过了他的保留空间后,vector搬家的动静是很大的。



Deque上场

代码部分可以照抄vector的,因为他们太像了。

测试结果:

  1. Noisy creations: 1

  2. Copy-Constructions: 1007

  3. Assignments: 0

  4. Destructions: 1008
复制代码

Beyond_ml评论:嗯,不错。不过那多出来的7个也不太好么。



List上场

代码部分继续照抄。

测试结果:

  1. Noisy creations: 1

  2. Copy-Constructions: 1000

  3. Assignments: 0

  4. Destructions: 1001
复制代码

Beyond_ml评论:perfect!非常好!满分。



第一局结束List胜出!



第二局 比一比随机访问的速度(访问速度以时钟周期作为标准)

咦?话音刚落,list怎么就举了白旗?哦,我想起来了,他不支持随机访问策略。也就是没有[ ]和at()操作。

测试程序:IndexingVsAt.cpp 插入一千个数据,用[ ]和at( )两种方法随机访问一百万次,比较时钟周期。

  1. //: C04:IndexingVsAt.cpp

  2. // Comparing "at()" to operator[]

  3. #include <vector>;

  4. #include <deque>;

  5. #include <iostream>;

  6. #include <ctime>;

  7. using namespace std;

  8. int main(int argc, char* argv[])

  9. {

  10.        long count = 1000;

  11.        int sz = 1000;

  12.       if(argc >;= 2) count = atoi(argv[1]);

  13.       if(argc >;= 3) sz = atoi(argv[2]);

  14.        vector<int>; vi(sz);

  15.        clock_t ticks = clock();

  16.        for(int i1 = 0; i1 < count; i1++)

  17.        for(int j = 0; j < sz; j++)

  18.        vi[j];

  19.        cout << "vector[]" << clock() - ticks << endl;

  20.        ticks = clock();

  21.        for(int i2 = 0; i2 < count; i2++)

  22.        for(int j = 0; j < sz; j++)

  23.        vi.at(j);

  24.        cout << "vector::at()" << clock()-ticks <<endl;

  25.        deque<int>; di(sz);

  26.        ticks = clock();

  27.        for(int i3 = 0; i3 < count; i3++)

  28.        for(int j = 0; j < sz; j++)

  29.        di[j];

  30.        cout << "deque[]" << clock() - ticks << endl;

  31.        ticks = clock();

  32.        for(int i4 = 0; i4 < count; i4++)

  33.        for(int j = 0; j < sz; j++)

  34.        di.at(j);

  35.        cout << "deque::at()" << clock()-ticks <<endl;

  36.        // Demonstrate at() when you go out of bounds:

  37.        //di.at(vi.size() + 1); error here.

  38. } ///:~
复制代码

测试结果:

  1. vector[]360000

  2. vector::at()790000

  3. deque[]1350000

  4. deque::at()1750000
复制代码

beyond_ml评论:果然是不必不知道,一比吓一跳。Vector以绝对优势胜出!



第三局 比后部插入速度以及iterator的访问速度

插入方法主要使用push_back。

然后再通过内部的iterator指针完成取数据的操作。

测试文件:

require.h  主要包含了一些文件操作。

  1. //: :require.h

  2. // Test for error conditions in programs

  3. // Local "using namespace std" for old compilers

  4. #ifndef REQUIRE_H

  5. #define REQUIRE_H

  6. #include <cstdio>;

  7. #include <cstdlib>;

  8. #include <fstream>;

  9. inline void require(bool requirement,const char* msg = "Requirement failed")

  10. {

  11.        using namespace std;

  12.        if (!requirement)

  13.        {

  14.               fputs(msg, stderr);

  15.               fputs("\n", stderr);

  16.               exit(1);

  17.        }

  18. }



  19. inline void requireArgs(int argc, int args,const char* msg = "Must use %d arguments")

  20. {

  21.        using namespace std;

  22.        if (argc != args + 1)

  23.        {

  24.               fprintf(stderr, msg, args);

  25.               fputs("\n", stderr);

  26.               exit(1);

  27.        }

  28. }



  29. inline void requireMinArgs(int argc, int minArgs,const char* msg ="Must use at least %d arguments")

  30. {

  31.        using namespace std;

  32.        if(argc < minArgs + 1)

  33.        {

  34.               fprintf(stderr, msg, minArgs);

  35.               fputs("\n", stderr);

  36.               exit(1);

  37.        }

  38. }



  39. inline void assure(std::ifstream& in,const char* filename = "")

  40. {

  41.        using namespace std;

  42.        if(!in)

  43.        {

  44.               fprintf(stderr,    "Could not open file %s\n", filename);

  45.               exit(1);

  46.        }

  47. }



  48. inline void assure(std::ofstream& in,const char* filename = "")

  49. {

  50.        using namespace std;

  51.        if(!in)

  52.        {

  53.               fprintf(stderr,    "Could not open file %s\n", filename);

  54.               exit(1);

  55.        }

  56. }

  57. #endif
复制代码

StringDeque.cpp 测试主程序

  1. //: C04:StringDeque.cpp

  2. // Converted from StringVector.cpp

  3. #include "require.h"

  4. #include <string>;

  5. #include <deque>;

  6. #include <vector>;

  7. #include <list>;

  8. #include <fstream>;

  9. #include <iostream>;

  10. #include <iterator>;

  11. #include <sstream>;

  12. #include <ctime>;

  13. using namespace std;



  14. int main(int argc, char* argv[])

  15. {

  16.        requireArgs(argc, 1);

  17.        ifstream in(argv[1]);

  18.        assure(in, argv[1]);

  19.        vector<string>; vstrings;

  20.        deque<string>; dstrings;

  21.        list<string>; lstrings;

  22.        string line;

  23.       

  24.        // Time reading into vector:

  25.        clock_t ticks = clock();

  26.        while(getline(in, line))

  27.        vstrings.push_back(line);

  28.        ticks = clock() - ticks;

  29.        cout << "Read into vector: " << ticks << endl;

  30.       

  31.        // Repeat for deque:

  32.        ifstream in2(argv[1]);

  33.        assure(in2, argv[1]);

  34.        ticks = clock();

  35.        while(getline(in2, line))

  36.        dstrings.push_back(line);

  37.        ticks = clock() - ticks;

  38.        cout << "Read into deque: " << ticks << endl;

  39.       

  40.        // Repeat for list:

  41.        ifstream in3(argv[1]);

  42.        assure(in3, argv[1]);

  43.        ticks = clock();

  44.        while(getline(in3, line))

  45.        lstrings.push_back(line);

  46.        ticks = clock() - ticks;

  47.        cout << "Read into list: " << ticks << endl;



  48.       

  49.        // Compare iteration

  50.        ofstream tmp1("tmp1.tmp"), tmp2("tmp2.tmp"), tmp3("tmp3.tmp");

  51.       

  52.        ticks = clock();

  53.        copy(vstrings.begin(), vstrings.end(),

  54.        ostream_iterator<string>;(tmp1, "\n"));

  55.        ticks = clock() - ticks;

  56.        cout << "Iterating vector: " << ticks << endl;

  57.       

  58.        ticks = clock();

  59.        copy(dstrings.begin(), dstrings.end(),

  60.        ostream_iterator<string>;(tmp2, "\n"));

  61.        ticks = clock() - ticks;

  62.        cout << "Iterating deqeue: " << ticks << endl;

  63.       

  64.        ticks = clock();

  65.        copy(lstrings.begin(), lstrings.end(),

  66.        ostream_iterator<string>;(tmp3, "\n"));

  67.        ticks = clock() - ticks;

  68.        cout << "Iterating list: " << ticks << endl;

  69.       

  70. } ///:~
复制代码

测试用的文件是一个三千行的文本。

测试结果:

  1. Read into vector: 690000

  2. Read into deque: 680000

  3. Read into list: 690000

  4. Iterating vector: 20000

  5. Iterating deqeue: 20000

  6. Iterating list: 10000
复制代码

测试用的文件是一个二千行的文本。

  1. Read into vector: 460000

  2. Read into deque: 460000

  3. Read into list: 440000

  4. Iterating vector: 10000

  5. Iterating deqeue: 10000

  6. Iterating list: 20000
复制代码

测试用的文件是一个一千行的文本。

测试结果:

  1. Read into vector: 230000

  2. Read into deque: 240000

  3. Read into list: 250000

  4. Iterating vector: 10000

  5. Iterating deqeue: 0

  6. Iterating list: 10000
复制代码

Beyond_ml的评论:这下就难了,怎么说呢?

在push_back的时候,显然文件越小,vector越占优,文件越大,list越占优。哈哈,开玩笑,如果作研究的都像我这样,那大家都不要干了,其实,这是和上面几个测试的结果分不开的,文件越大,vector越费力,原因很简单,他要不停的开辟新的内存空间来给自己搬家,而deque就好的多,因为他不必搬家,他只是需要小范围的重新排列。而list就更每问题了,他的内存空间本来就是离散的。这下你能明白了吧?

所以作为函数本身的运行速度是没有大差别的,但现在看来,如果牵扯上其它因素,就要令说了。

而读数据的速度来看,list的表现十分让人迷惑不解对此,我还想不到什么好的解释,也许和程序运行时主机的内存状态有关吧。Vector和list的表现可以说是不分伯仲,但我个人的观点是vector肯定要好一些,因为他的内存是连续的。

所以第三局,三者的表现各有千秋。

论坛徽章:
0
2 [报告]
发表于 2003-03-26 03:24 |只看该作者

C++ 常用模板武道会 第一场 上

呵呵,不错,颇有当年C++专栏小品的味道。。。

论坛徽章:
0
3 [报告]
发表于 2003-03-26 11:46 |只看该作者

C++ 常用模板武道会 第一场 上

不错~~继续啊~

论坛徽章:
0
4 [报告]
发表于 2003-03-26 12:56 |只看该作者

C++ 常用模板武道会 第一场 上

到google上找吧

我在赋个网站上打不到下集

等找到了再帖出来
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP