找回密码
 FreeOZ用户注册
查看: 2131|回复: 10
打印 上一主题 下一主题

[论坛技术] C++ Allocator的一个简单实验

[复制链接]
跳转到指定楼层
1#
发表于 14-5-2009 01:41:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?FreeOZ用户注册

x
这两天都在写关于operator new()的文章(晚些时候发上来),其中遇到Allocator想深入分析一下,
就写了下面的这些代码。

代码中没有什么有创意的东西,主要做下面几件事:

1. 实现一个基类Base和一个派生类Derived
2. 实现一个MyAllocator类,这个类主要通过proxy设计模式,把所有的方法都代理给系统原来的
allocator<T>,主要是加入一些屏显信息,用来分析程序。注意,我这里选择组合,而不是继承。
3. 使用vector,配置它使用我自己的MyAllocator类

就是一段代码,做一个基础,希望有兴趣的同学一起讨论一下。我另外在写一篇关于operator new()的文章,
会用到这个例子。
  1.   1 #include <vector>
  2.   2 #include <iostream>
  3.   3
  4.   4 using std::cout;
  5.   5 using std::vector;
  6.   6 using std::endl;
  7.   7 using std::allocator;
  8.   8
  9.   9 template<class T>
  10. 10 class MyAllocator
  11. 11 {
  12. 12 private:
  13. 13     allocator<T> * alloc;
  14. 14
  15. 15 public:
  16. 16     template<class T1>
  17. 17         struct rebind { typedef MyAllocator<T1> other; };
  18. 18
  19. 19       typedef size_t     size_type;
  20. 20       typedef ptrdiff_t  difference_type;
  21. 21       typedef T*       pointer;
  22. 22       typedef const T* const_pointer;
  23. 23       typedef T&       reference;
  24. 24       typedef const T& const_reference;
  25. 25       typedef T        value_type;
  26. 26  
  27. 27     MyAllocator<T>() throw() : alloc(new allocator<T>) { cout << "MyAllocator : construct myself" << endl; }
  28. 28     MyAllocator<T>(MyAllocator<T> const & ma) throw() : alloc(new allocator<T>(* ma.alloc))
  29. 29     {
  30. 30         cout << "MyAllocator : copy constructor" << endl;
  31. 31     }
  32. 32     ~MyAllocator<T>() throw() { delete alloc; cout << "Destruct myself" << endl; }
  33. 33
  34. 34     pointer allocate(size_type __n, const void* = 0)
  35. 35     {
  36. 36         cout << "MyAllocator : allocate, size = " << __n << endl;
  37. 37         return alloc->allocate(__n);
  38. 38     }
  39. 39
  40. 40     void deallocate(pointer __p, size_type)
  41. 41     {
  42. 42         cout << "MyAllocator : deallocate" << endl;
  43. 43         alloc->deallocate(__p, sizeof(size_type));
  44. 44     }
  45. 45
  46. 46     void construct(pointer __p, const T & a)
  47. 47     {
  48. 48         cout << "MyAllocator : construct, typeid = " << " size = " << sizeof(a) << endl;
  49. 49         alloc->construct(__p, a);
  50. 50     }
  51. 51
  52. 52     void destroy(pointer __p)
  53. 53     {
  54. 54         cout << "MyAllocator : destroy" << endl;
  55. 55         alloc->destroy(__p);
  56. 56     }
  57. 57 };
  58. 58
  59. 59
  60. 60 class Base
  61. 61 {
  62. 62     int x1;
  63. 63 public:
  64. 64     Base() : x1(0) { cout << "Constructor of Base" << endl; }
  65. 65     Base(Base const & b) : x1(b.x1) { cout << "Copy constructor of Base" << endl; }
  66. 66     virtual ~Base() { hello(); };
  67. 67     virtual void hello();
  68. 68 };
  69. 69
  70. 70 void Base::hello()
  71. 71 {
  72. 72     cout << "Hello from Base" << endl;
  73. 73 }
  74. 74
  75. 75 class Derived: public Base
  76. 76 {
  77. 77     int x2;
  78. 78 public:
  79. 79     Derived() : x2(1) { cout << "Constructor of Derived" << endl; }
  80. 80     Derived(Derived const & d) : x2(d.x2), Base(d) { cout << "Copy constructor of Derived" << endl; }
  81. 81     virtual ~Derived() { hello(); }
  82. 82     virtual void hello();
  83. 83 };
  84. 84
  85. 85 void Derived::hello()
  86. 86 {
  87. 87     cout << "Hello from Derived" << endl;
  88. 88 }
  89. 89
  90. 90 int main()
  91. 91 {
  92. 92     vector<Base, MyAllocator<Base> > v;
  93. 93     cout << "Begin to push_back objects" << endl;
  94. 94     cout << "push back Base" << endl;
  95. 95     v.push_back(Base());
  96. 96     cout << "push back Derived" << endl;
  97. 97     v.push_back(Derived());
  98. 98
  99. 99     cout << "Begin to pop_back objects" << endl;
  100. 100     cout << "pop_back Derived" << endl;
  101. 101     v.pop_back();
  102. 102     cout << "pop_back Base" << endl;
  103. 103     v.pop_back();
  104. 104
  105. 105     return 0;
  106. 106 }
复制代码
运行结果:
  1.   1 MyAllocator : construct myself
  2.   2 MyAllocator : copy constructor
  3.   3 Destruct myself
  4.   4 Begin to push_back objects
  5.   5 push back Base
  6.   6 Constructor of Base
  7.   7 MyAllocator : allocate, size = 1
  8.   8 MyAllocator : copy constructor
  9.   9 Destruct myself
  10. 10 MyAllocator : construct, typeid =  size = 8
  11. 11 Copy constructor of Base
  12. 12 MyAllocator : copy constructor
  13. 13 Destruct myself
  14. 14 MyAllocator : copy constructor
  15. 15 Destruct myself
  16. 16 Hello from Base
  17. 17 push back Derived
  18. 18 Constructor of Base
  19. 19 Constructor of Derived
  20. 20 MyAllocator : allocate, size = 2
  21. 21 MyAllocator : copy constructor
  22. 22 MyAllocator : construct, typeid =  size = 8
  23. 23 Copy constructor of Base
  24. 24 Destruct myself
  25. 25 MyAllocator : construct, typeid =  size = 8
  26. 26 Copy constructor of Base
  27. 27 MyAllocator : copy constructor
  28. 28 Destruct myself
  29. 29 MyAllocator : copy constructor
  30. 30 MyAllocator : destroy
  31. 31 Hello from Base
  32. 32 Destruct myself
  33. 33 MyAllocator : deallocate
  34. 34 Hello from Derived
  35. 35 Hello from Base
  36. 36 Begin to pop_back objects
  37. 37 pop_back Derived
  38. 38 MyAllocator : destroy
  39. 39 Hello from Base
  40. 40 pop_back Base
  41. 41 MyAllocator : destroy
  42. 42 Hello from Base
  43. 43 MyAllocator : copy constructor
  44. 44 Destruct myself
  45. 45 MyAllocator : deallocate
  46. 46 Destruct myself
复制代码
回复  

使用道具 举报

2#
 楼主| 发表于 14-5-2009 02:27:57 | 只看该作者

实验结果比较难看,改一下程序

主要的改动是:

采用一个特定的输出接口,通过模板的使用,以及单例模式,
显示“每一行”输出的行号,类名,以及对象的Serial Number

这样就可以很好的跟踪相应的对象走向了。
  1.   1 #include <vector>
  2.   2 #include <iostream>
  3.   3
  4.   4 using std::cout;
  5.   5 using std::vector;
  6.   6 using std::endl;
  7.   7 using std::allocator;
  8.   8 using std::ostream;
  9.   9     
  10. 10 class Single
  11. 11 {   
  12. 12     static Single * instance;
  13. 13     int curr_number;
  14. 14     
  15. 15     Single() : curr_number(0) {}
  16. 16
  17. 17 public:
  18. 18     static Single * getSingle()
  19. 19     {
  20. 20         if(instance == NULL)
  21. 21             instance = new Single();
  22. 22         return instance;
  23. 23     }
  24. 24     int getNumber()
  25. 25     {
  26. 26         return ++ curr_number;
  27. 27     }
  28. 28 };  
  29. 29         
  30. 30 Single * Single::instance = NULL;
  31. 31     
  32. 32 static int count = 0;
  33. 33 template<class T>
  34. 34 ostream & getOutput(ostream & os, T const & t)
  35. 35 {
  36. 36
  37. 37     os << ++count << "\t" << T::class_name << " [ SN= " << t.serial_number << " ] ";
  38. 38     return os;
  39. 39 }
  40. 40     
  41. 41 template<class T>
  42. 42 class MyAllocator
  43. 43 {
  44. 44 private:
  45. 45     allocator<T> * alloc;
  46. 46     int            serial_number;
  47. 47
  48. 48     static const char * class_name;
  49. 49
  50. 50     template<class Tx> friend ostream & getOutput(ostream & os, Tx const & a);
  51. 51
  52. 52 public:
  53. 53     template<class T1>
  54. 54         struct rebind { typedef MyAllocator<T1> other; };
  55. 55
  56. 56       typedef size_t     size_type;
  57. 57       typedef ptrdiff_t  difference_type;
  58. 58       typedef T*       pointer;
  59. 59       typedef const T* const_pointer;
  60. 60       typedef T&       reference;
  61. 61       typedef const T& const_reference;
  62. 62       typedef T        value_type;
  63. 63
  64. 64     MyAllocator<T>() throw() : alloc(new allocator<T>), serial_number(Single::getSingle()->getNumber())
  65. 65     { getOutput(cout, * this) << "MyAllocator : construct myself" << endl; }
  66. 66
  67. 67     MyAllocator<T>(MyAllocator<T> const & ma) throw() :
  68. 68         alloc(new allocator<T>(* ma.alloc)),
  69. 69         serial_number(Single::getSingle()->getNumber())
  70. 70     {
  71. 71         getOutput(cout, * this) << "MyAllocator : copy constructor" << endl;
  72. 72     }
  73. 73     ~MyAllocator<T>() throw()
  74. 74     {
  75. 75         delete alloc;
  76. 76         getOutput(cout, * this)<< "Destruct myself" << endl;
  77. 77     }
  78. 78
  79. 79     pointer allocate(size_type __n, const void* = 0)
  80. 80     {
  81. 81         getOutput(cout, * this) << "MyAllocator : allocate, size = " << __n << endl;
  82. 82         return alloc->allocate(__n);
  83. 83     }
  84. 84
  85. 85     void deallocate(pointer __p, size_type)
  86. 86     {
  87. 87         getOutput(cout, * this) << "MyAllocator : deallocate" << endl;
  88. 88         alloc->deallocate(__p, sizeof(size_type));
  89. 89     }
  90. 90
  91. 91     void construct(pointer __p, const T & a)
  92. 92     {
  93. 93         getOutput(cout, * this) << "MyAllocator : construct, typeid = " << " size = " << sizeof(a) << endl;
  94. 94         alloc->construct(__p, a);
  95. 95     }
  96. 96
  97. 97     void destroy(pointer __p)
  98. 98     {
  99. 99         getOutput(cout, * this) << "MyAllocator : destroy" << endl;
  100. 100         alloc->destroy(__p);
  101. 101     }
  102. 102 };
  103. 103
  104. 104 template<class T> const char * MyAllocator<T>::class_name = "MyAllocator";
  105. 105
  106. 106 class Base
  107. 107 {
  108. 108     int x1;
  109. 109     static const char * class_name;
  110. 110 protected:
  111. 111     int serial_number;
  112. 112
  113. 113     template<class T> friend ostream & getOutput(ostream & os, T const &);
  114. 114
  115. 115 public:
  116. 116     Base() : x1(0), serial_number(Single::getSingle()->getNumber())
  117. 117     { getOutput(cout, * this) << "Constructor of Base" << endl; }
  118. 118
  119. 119     Base(Base const & b) : x1(b.x1), serial_number(Single::getSingle()->getNumber())
  120. 120     { getOutput(cout, * this) << "Copy constructor of Base" << endl; }
  121. 121
  122. 122     virtual ~Base() { hello(); };
  123. 123     virtual void hello();
  124. 124 };
  125. 125
  126. 126 const char * Base::class_name = "Base";
  127. 127
  128. 128
  129. 129
  130. 130 void Base::hello()
  131. 131 {
  132. 132     getOutput(cout, * this) << "Hello from Base" << endl;
  133. 133 }
  134. 134
  135. 135 class Derived: public Base
  136. 136 {
  137. 137     int x2;
  138. 138     static const char * class_name;
  139. 139
  140. 140     template<class T> friend ostream & getOutput(ostream & os, T const &);
  141. 141     //friend ostream & getOutput(ostream & os, Derived const &);
  142. 142     //friend ostream & getOutput(ostream & os, Base const &);
  143. 143
  144. 144 public:
  145. 145     Derived() : x2(1)
  146. 146     {
  147. 147         getOutput(cout, * this) << "Constructor of Derived" << endl;
  148. 148     }
  149. 149
  150. 150     Derived(Derived const & d) : x2(d.x2), Base(d)
  151. 151     {
  152. 152         getOutput(cout, * this) << "Copy constructor of Derived" << endl;
  153. 153     }
  154. 154
  155. 155     virtual ~Derived() { hello(); }
  156. 156     virtual void hello();
  157. 157 };
  158. 158
  159. 159 const char * Derived::class_name = "Derived";
  160. 160
  161. 161 void Derived::hello()
  162. 162 {
  163. 163     getOutput(cout, * this) << "Hello from Derived" << endl;
  164. 164 }
  165. 165
  166. 166 int main()
  167. 167 {
  168. 168     vector<Base, MyAllocator<Base> > v;
  169. 169     cout << "Begin to push_back objects" << endl;
  170. 170     cout << "push back Base" << endl;
  171. 171     v.push_back(Base());
  172. 172     cout << "push back Derived" << endl;
  173. 173     v.push_back(Derived());
  174. 174
  175. 175     cout << "Begin to pop_back objects" << endl;
  176. 176     cout << "pop_back Derived" << endl;
  177. 177     v.pop_back();
  178. 178     cout << "pop_back Base" << endl;
  179. 179     v.pop_back();
  180. 180
  181. 181     return 0;
  182. 182 }
复制代码
输出:
  1. 1       MyAllocator [ SN= 1 ] MyAllocator : construct myself
  2. 2       MyAllocator [ SN= 2 ] MyAllocator : copy constructor
  3. 3       MyAllocator [ SN= 1 ] Destruct myself
  4. Begin to push_back objects
  5. push back Base
  6. 4       Base [ SN= 3 ] Constructor of Base
  7. 5       MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 1
  8. 6       MyAllocator [ SN= 4 ] MyAllocator : copy constructor
  9. 7       MyAllocator [ SN= 4 ] Destruct myself
  10. 8       MyAllocator [ SN= 2 ] MyAllocator : construct, typeid =  size = 12
  11. 9       Base [ SN= 5 ] Copy constructor of Base
  12. 10      MyAllocator [ SN= 6 ] MyAllocator : copy constructor
  13. 11      MyAllocator [ SN= 6 ] Destruct myself
  14. 12      MyAllocator [ SN= 7 ] MyAllocator : copy constructor
  15. 13      MyAllocator [ SN= 7 ] Destruct myself
  16. 14      Base [ SN= 3 ] Hello from Base
  17. push back Derived
  18. 15      Base [ SN= 8 ] Constructor of Base
  19. 16      Derived [ SN= 8 ] Constructor of Derived
  20. 17      MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 2
  21. 18      MyAllocator [ SN= 9 ] MyAllocator : copy constructor
  22. 19      MyAllocator [ SN= 9 ] MyAllocator : construct, typeid =  size = 12
  23. 20      Base [ SN= 10 ] Copy constructor of Base
  24. 21      MyAllocator [ SN= 9 ] Destruct myself
  25. 22      MyAllocator [ SN= 2 ] MyAllocator : construct, typeid =  size = 12
  26. 23      Base [ SN= 11 ] Copy constructor of Base
  27. 24      MyAllocator [ SN= 12 ] MyAllocator : copy constructor
  28. 25      MyAllocator [ SN= 12 ] Destruct myself
  29. 26      MyAllocator [ SN= 13 ] MyAllocator : copy constructor
  30. 27      MyAllocator [ SN= 13 ] MyAllocator : destroy
  31. 28      Base [ SN= 5 ] Hello from Base
  32. 29      MyAllocator [ SN= 13 ] Destruct myself
  33. 30      MyAllocator [ SN= 2 ] MyAllocator : deallocate
  34. 31      Derived [ SN= 8 ] Hello from Derived
  35. 32      Base [ SN= 8 ] Hello from Base
  36. Begin to pop_back objects
  37. pop_back Derived
  38. 33      MyAllocator [ SN= 2 ] MyAllocator : destroy
  39. 34      Base [ SN= 11 ] Hello from Base
  40. pop_back Base
  41. 35      MyAllocator [ SN= 2 ] MyAllocator : destroy
  42. 36      Base [ SN= 10 ] Hello from Base
  43. 37      MyAllocator [ SN= 14 ] MyAllocator : copy constructor
  44. 38      MyAllocator [ SN= 14 ] Destruct myself
  45. 39      MyAllocator [ SN= 2 ] MyAllocator : deallocate
  46. 40      MyAllocator [ SN= 2 ] Destruct myself
复制代码

[ 本帖最后由 key 于 14-5-2009 01:30 编辑 ]
回复  

使用道具 举报

3#
 楼主| 发表于 14-5-2009 20:40:29 | 只看该作者

真可怜,没有人理我这个专题

上面的实验程序出来的数据还不是太完全,根据需要,我又改了一些地方,各位同学可以按照自己
的想法在上面改。

我改的地方包括:

1. 读出Base和Derived的大小。采用sizeof()即可。(如果你要质疑sizeof()在这里的作用的话,请先看相关的原代码)
2. push_bask()和pop_back()多几个对象,我把对象数扩展到8个

分析一:allocate函数和vector的结合

从C++ Primer 4th Edition第18章(后面说到C++ Primer我都是指第4版)有关于vector的一个简化实现。
现在我们就参考这个简化实现吧。

我们用push_back()向vector添加元素,而push_back()会做两件事:
1. 扩展内存,如果需要的话
2. 调用allocator.construct来构造一个对象,实质是通过copy constructor来clone一个对象
3. 把对象添到vector的第一个空闲位置

这里,第一步是我所关心的。vector::reallocate()就是做这个事。其扩展算法简单来说就是加倍法:
以原有元素个数为基础加一倍空间。这里并没有采用类似C语方中的realloc()函数,而是直接产生
一段新内存空间,然后批量把对象copy过去。由于uninitialized_copy()不是allocator中的函数,
本实验没有跟踪到这部分内容。有机会再跟踪。

vector::reallocate()不单用于处理内存扩展,还用于处理vector::pop_back()时的内存释放,
以回收空间。

现在来看测试结果:
  1. 5       MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 1
  2. 17      MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 2
复制代码
这是两次内存扩展的证明。采用的是MyAllocator 02这个对象来操作。可能这里看得还不明显,
但当我把push_back()的对象增加一些后,这个“加倍”扩展的算法就很明显了。

我自己的另一项实验结果是:
  1. 5       MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 1 sizeof(T) = 12
  2. 17      MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 2 sizeof(T) = 12
  3. 34      MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 4 sizeof(T) = 12
  4. 60      MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 8 sizeof(T) = 12
复制代码
你在这里可能就已经注意到,一直用来做内存扩展的都是SN = 02的这个Allocator。而SN = 01的
那个Allocator早就下班去吃饭了。这是Vector_Impl这东西在搞的鬼,它从一开始就用了refine来
克隆了一个新的Allocator,然后让原来的Allocator下班去。

其实我们在实验过程中还见过很多新的Allocator对象,但用来做allocate()的就一直只是这个二号仔。

至于生成的capacity后把原来的空间释放的部分在这里也可以见到:
  1. 30      MyAllocator [ SN= 2 ] MyAllocator : deallocate
复制代码
因为这里只做了一次扩展,还不明显。我的新实验可以看到更多:
  1. 30      MyAllocator [ SN= 2 ] MyAllocator : deallocate
  2. 51      MyAllocator [ SN= 2 ] MyAllocator : deallocate
  3. 85      MyAllocator [ SN= 2 ] MyAllocator : deallocate
复制代码
三次扩展,对应三次释放原来的空间。
回复  

使用道具 举报

4#
 楼主| 发表于 14-5-2009 21:15:16 | 只看该作者

疑问出来了:对象所需空间的大小,原来如此

表面看起来,这个实验没有什么奇怪。但奇怪的问题马上可以看到了。

有没有注意到,尤其是在我新的实验程序结果中,我特别打印了“大小”,可以看得更清楚:
  1. 5       MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 1 sizeof(T) = 12
  2. 17      MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 2 sizeof(T) = 12
  3. 34      MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 4 sizeof(T) = 12
  4. 60      MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 8 sizeof(T) = 12
复制代码
对于Base来说,size为12,而对于Derived来说,size为16,但allocator一直做的就只是设置size=12的内存。
请看allocator::allocate()的实现:
  1. 80       // NB: __n is permitted to be 0.  The C++ standard says nothing
  2. 81       // about what the return value is when __n == 0.
  3. 82       pointer
  4. 83       allocate(size_type __n, const void* = 0)
  5. 84       {
  6. 85         if (__builtin_expect(__n > this->max_size(), false))
  7. 86           std::__throw_bad_alloc();
  8. 87
  9. 88         return static_cast<_Tp*>(::operator new(__n * [b]sizeof(_Tp)[/b]));
  10. 89       }
复制代码
注意88行,这里采用静态的sizeof(_Tp)来分配内存。

那问题就来了,用基类为基础分配的内存怎样容下子类?
答案是:vector<Base>根据就没有把子类放进去,而是利用了Base::Base(Base const &)这个拷贝构造器,
用Derived对象构造了一个基类!!!
  1. push back Derived
  2. 15      Base [ SN= 8 ] Constructor of Base
  3. 16      Derived [ SN= 8 ] Constructor of Derived
  4. 17      MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 2
  5. 18      MyAllocator [ SN= 9 ] MyAllocator : copy constructor
  6. 19      MyAllocator [ SN= 9 ] MyAllocator : construct, typeid =  size = 12
  7. [b]20      Base [ SN= 10 ] Copy constructor of Base[/b]
  8. 21      MyAllocator [ SN= 9 ] Destruct myself
  9. 22      MyAllocator [ SN= 2 ] MyAllocator : construct, typeid =  size = 12
  10. [b]23      Base [ SN= 11 ] Copy constructor of Base[/b]
  11. 24      MyAllocator [ SN= 12 ] MyAllocator : copy constructor
  12. 25      MyAllocator [ SN= 12 ] Destruct myself
  13. 26      MyAllocator [ SN= 13 ] MyAllocator : copy constructor
  14. 27      MyAllocator [ SN= 13 ] MyAllocator : destroy
  15. [b]28      Base [ SN= 5 ] Hello from Base[/b]
  16. 29      MyAllocator [ SN= 13 ] Destruct myself
  17. 30      MyAllocator [ SN= 2 ] MyAllocator : deallocate
  18. 31      Derived [ SN= 8 ] Hello from Derived
  19. 32      Base [ SN= 8 ] Hello from Base
复制代码
注意20, 23, 28三行信息。其中第20行是把原来sn=5的Base对象copy到新空间里,23是用Derived对象构造
新的Base对象,而28则是释放原来的sn=5的Base对象。

显然,这里是依据:T::T(T const &)这个构造函数来做操作。由于子类是is-a的关系,所以这个操作是安全的。
也就是说,通过vector<T>指定的向量中,是不可以通过继承关系,建立一个多层次的对象向量空间。

哦!万事休矣!
回复  

使用道具 举报

5#
 楼主| 发表于 14-5-2009 21:27:17 | 只看该作者

老实说,我没有想到分析到最后是这个结果

谁能告诉我STL有文档说这方面的问题?

我对Allocator的分析,主要原因是因为我觉得从Allocator的设计上存在空间分配的问题。
因为按照Allocator的接口定义,内存空间的分配都是 sizeof(value_type) * n的,这样的结果
在实现派生类的装载时就会出现问题。

我的目标是想看看Allocator怎样巧妙的把这个空间分配解决掉,
没想到最后是这样的一个结果。
回复  

使用道具 举报

6#
发表于 14-5-2009 21:41:19 | 只看该作者

回复 #5 key 的帖子

怎么会觉得奇怪呢,STL的容器就是这样的行为啊。

vector<Base> 里就是放的Base的值, 而且只能放Base的值,这是STL Container作为值容器的性质决定的啊,想要在STL容器中体现多态只能用vector<Base*>这样的方式,或者用Boost Pointer Container或者Boost.Intrusive这样的容器.
回复  

使用道具 举报

7#
发表于 14-5-2009 21:45:51 | 只看该作者

关于Boost.Intrusive的介绍

Boost.Intrusive is a library presenting some intrusive containers to the world of C++. Intrusive containers are special containers that offer better performance and exception safety guarantees than non-intrusive containers (like STL containers).

Boost.Intrusive 是一个将介入式容器引入到C++世界的库。介入式容器是一种特殊的容器,它提供比非介入式容器(如STL容器) 更好的性能 和异常安全保证。
The performance benefits of intrusive containers makes them ideal as a building block to efficiently construct complex containers like multi-index containers or to design high performance code like memory allocation algorithms.

介入式容器的性能优点使得它们成为一种构建块,用以高效地构造复杂容器,如多索引容器,或者用以设计高性能代码,如内存分配算法。
  
While intrusive containers were and are widely used in C, they became more and more forgotten in C++ due to the presence of the standard containers which don't support intrusive techniques.Boost.Intrusive not only reintroduces this technique to C++, but also encapsulates the implementation in STL-like interfaces. Hence anyone familiar with standard containers can easily use Boost.Intrusive.

虽然介入式容器在C中被广泛使用,但是在C++中却被日渐遗忘,这是由于不支持介入式技术的标准容器的出现。Boost.Intrusive 不仅重新将这一技术引入到C++,而且还将实现封装为类似于STL的接口。所以每一个熟悉标准容器的人都可以很容易地使用 Boost.Intrusive
The main difference between intrusive containers and non-intrusive containers is that in C++ non-intrusive containers store copies of values passed by the user. Containers use the Allocator template parameter to allocate the stored values:

介入式容器与非介入式容器的主要区别在于,在C++非介入式容器中保存的是由用户传入的值的拷贝。容器使用 Allocator 模块参数来分配被保存的值。
On the other hand, an intrusive container does not store copies of passed objects, but it stores the objects themselves. The additional data needed to insert the object in the container must be provided by the object itself. For example, to insert MyClass in an intrusive container that implements a linked list, MyClass must contain the needed next and previous pointers:

另一方面,介入式容器则不保存传入对象的拷贝,而是保存对象本身。需要插入到容器中对象里的额外数据必须由对象本身提供。
Semantically, a Boost.Intrusive container is similar to a STL container holding pointers to objects. That is, if you have an intrusive list holding objects of type T, then std::list<T*> would allow you to do quite the same operations (maintaining and navigating a set of objects of type T and types derived from it).

语义上,一个 Boost.Intrusive 容器类似于一个保存对象指针的STL容器。即,如果你有一个保存类型 T 的对象的介入式 list,那么 std::list<T*> 也允许你做完全相同的操作(维护和操纵一组类型为 T 或其派生类的对象)。
回复  

使用道具 举报

8#
发表于 14-5-2009 21:51:06 | 只看该作者

非介入式容器(如STL)的一些限制

A non-intrusive container has some limitations:
非介入式容器有一些限制:
  • An object can only belong to one container: If you want to share an object between two containers, you either have to store multiple copies of those objects or you need to use containers of pointers: std::list<Object*>.
    一个对象只能属于一个容器:如果你想在两个容器间共享一个对象,你要么必须保存多份拷贝,要么就需要使用指针容器:std::list<Object*>.
  • The use of dynamic allocation to create copies of passed values can be a performance and size bottleneck in some applications. Normally, dynamic allocation imposes a size overhead for each allocation to store bookkeeping information and a synchronization to protected concurrent allocation from different threads.
    在一些应用中,为传入值创建拷贝所用的动态分配可能成为性能和空间的瓶颈。通常,动态分配需要为每次分配增加一些空间开销来保簿记信息,还有同步操作以保护来自不同线程的并发分配。
  • Only copies of objects are stored in non-intrusive containers. Hence copy or move constructors and copy or move assignment operators are required. Non-copyable and non-movable objects can't be stored in non-intrusive containers.
    在非介入式容器中只能保存对象的拷贝。因此,需要复制或移动构造函数和复制或移动赋值操作符。不可复制和不可移动的对象不能保存在非介入式容器中。
  • It's not possible to store a derived object in a STL-container while retaining its original type.
    不可能将一个派生类对象保存在STL容器中而且保留它的原本类型。 // to key:你就是迷惑在这里了
回复  

使用道具 举报

9#
发表于 14-5-2009 21:51:48 | 只看该作者

介入式容器优缺点总结

问题
介入式
非介入式

内存管理

外部

内部,通过分配器

插入/删除的时间

较快

较慢

内存位置

较好

较差

可否以值方式保存不可复制和不可移动的对象

可以

不可

异常保证

较好
较差

从值计算迭代器

常量复杂度

非常量复杂度

插入/删除操作的可预见性




内存占用
最少

比最少多

以值方式插入的对象保持多态行为


不是(切片)
用户必须修改值的定义以便插入



容器是可复制的


被插入对象的生存期由谁管理
用户(较复杂)
容器(较不复杂)
不使用容器也能破坏容器的不变式

较容易
较难(只有使用指针容器才会)
线程安全性分析

较难
较容易
回复  

使用道具 举报

10#
 楼主| 发表于 14-5-2009 23:44:18 | 只看该作者
原帖由 coredump 于 14-5-2009 20:41 发表
怎么会觉得奇怪呢,STL的容器就是这样的行为啊。

vector 里就是放的Base的值, 而且只能放Base的值,这是STL Container作为值容器的性质决定的啊,想要在STL容器中体现多态只能用vector这样的方式,或者用Boost P ...


搞了半天,忘了自己在传值了
回复  

使用道具 举报

11#
发表于 15-5-2009 18:06:58 | 只看该作者
太强了。。。。。。
回复  

使用道具 举报

您需要登录后才可以回帖 登录 | FreeOZ用户注册

本版积分规则

小黑屋|手机版|Archiver|FreeOZ论坛

GMT+11, 2-11-2024 06:40 , Processed in 0.046016 second(s), 26 queries , Gzip On, Redis On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表