key 发表于 14-5-2009 01:41:28

C++ Allocator的一个简单实验

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

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

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

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

key 发表于 14-5-2009 02:27:57

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

主要的改动是:

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

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

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

key 发表于 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()时的内存释放,
以回收空间。

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

我自己的另一项实验结果是:5       MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 1 sizeof(T) = 12
17      MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 2 sizeof(T) = 12
34      MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 4 sizeof(T) = 12
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后把原来的空间释放的部分在这里也可以见到:30      MyAllocator [ SN= 2 ] MyAllocator : deallocate因为这里只做了一次扩展,还不明显。我的新实验可以看到更多:30      MyAllocator [ SN= 2 ] MyAllocator : deallocate
51      MyAllocator [ SN= 2 ] MyAllocator : deallocate
85      MyAllocator [ SN= 2 ] MyAllocator : deallocate三次扩展,对应三次释放原来的空间。

key 发表于 14-5-2009 21:15:16

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

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

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

那问题就来了,用基类为基础分配的内存怎样容下子类?
答案是:vector<Base>根据就没有把子类放进去,而是利用了Base::Base(Base const &)这个拷贝构造器,
用Derived对象构造了一个基类!!!push back Derived
15      Base [ SN= 8 ] Constructor of Base
16      Derived [ SN= 8 ] Constructor of Derived
17      MyAllocator [ SN= 2 ] MyAllocator : allocate, size = 2
18      MyAllocator [ SN= 9 ] MyAllocator : copy constructor
19      MyAllocator [ SN= 9 ] MyAllocator : construct, typeid =size = 12
20      Base [ SN= 10 ] Copy constructor of Base
21      MyAllocator [ SN= 9 ] Destruct myself
22      MyAllocator [ SN= 2 ] MyAllocator : construct, typeid =size = 12
23      Base [ SN= 11 ] Copy constructor of Base
24      MyAllocator [ SN= 12 ] MyAllocator : copy constructor
25      MyAllocator [ SN= 12 ] Destruct myself
26      MyAllocator [ SN= 13 ] MyAllocator : copy constructor
27      MyAllocator [ SN= 13 ] MyAllocator : destroy
28      Base [ SN= 5 ] Hello from Base
29      MyAllocator [ SN= 13 ] Destruct myself
30      MyAllocator [ SN= 2 ] MyAllocator : deallocate
31      Derived [ SN= 8 ] Hello from Derived
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>指定的向量中,是不可以通过继承关系,建立一个多层次的对象向量空间。

哦!万事休矣!

key 发表于 14-5-2009 21:27:17

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

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

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

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

coredump 发表于 14-5-2009 21:41:19

回复 #5 key 的帖子

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

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

coredump 发表于 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 或其派生类的对象)。

coredump 发表于 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:你就是迷惑在这里了

coredump 发表于 14-5-2009 21:51:48

介入式容器优缺点总结

问题
介入式
非介入式

内存管理

外部

内部,通过分配器

插入/删除的时间

较快

较慢

内存位置

较好

较差

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

可以

不可

异常保证

较好
较差

从值计算迭代器

常量复杂度

非常量复杂度

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




内存占用
最少

比最少多

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


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



容器是可复制的


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

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

较难
较容易

key 发表于 14-5-2009 23:44:18

原帖由 coredump 于 14-5-2009 20:41 发表 http://www.freeoz.org/forum/images/common/back.gif
怎么会觉得奇怪呢,STL的容器就是这样的行为啊。

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

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

monster2006 发表于 15-5-2009 18:06:58

太强了。。。。。。
页: [1]
查看完整版本: C++ Allocator的一个简单实验