描述:
stl(指vs2008的stl)中的vector在分配对象的时候,出现一些意想不到的事情.
比如:
class A
{};
vector<A> a(10);
这样就可以构造出一个有10个元素类型为A的vector, 考察其构造过程,就会发现其实并不是构造了10个A,而是构造了11个A,然后再析构1个.
这个主要是实现的问题,看源码就知道,
explicit vector(size_type _Count)
: _Mybase()
{ // construct from _Count * _Ty()
_Construct_n(_Count, _Ty());
}
void _Construct_n(size_type _Count, const _Ty& _Val)
因为_Construct_n在构造过程中要调用class A 的拷贝构造函数,所以第2个参数为一个_Ty类型,也就是上面的class A,的一个实例.
于是就先构造出一个临时的A,传给_Construct_n,构造函数完成后,析构掉.
感觉主要原因实现时偷懒了吧. 可以在Alloc里面增加一个使用 ::new(_p) _Ty() (默认构造函数)的函数就好了,为什么非要用 ::new(_p) _Ty(_Val) (拷贝构造函数) 呢.
同样的问题,就是在
vector<A> x(10);
vector<A> y = x;
这个时候y=x调用的是vector的拷贝构造函数,按照C++的"习惯",容器里面的对象也应该使用拷贝构造函数构造,但是不幸的是由于实现的时候是用copy(x.begin(), x.end(), y.begin()) 的,所以就全部用 operator= 来做了.
首先可以看到这是两个问题,第一个问题,我看了SGI STL和C++标准确实是这样实现的,并没有提供对默认构造函数的placement new。
C++ standard 20.4.1.1
void construct(pointer p, const_reference val);
Returns: new((void *)p) T(val)
但是C++标准描述的是DefaultAllocator,但是用户可以自己开发Allocator。那么问题就是C++标准为什么这样定义?
我觉得是因为性能,有人认为多了一次默认构造会造成性能下降,其实STL考虑的是给使用者Copy On Write的机会。如果STL容器中插入的是带有引用计数功能的对象。那么拷贝构造函数就会比构造一个新的对象快很多。因为对象内部的成员函数不需要进行分配内存和初始化,只是引用计数发生变化而已。CString就是这样实现的。具体参见More Effective C++或者 Exceptional C++ 和MSDN。
关于第二个问题,我觉得是为了保证强烈异常安全。
vector<A> x(10);
vector<A> y = x;
将拷贝构造分为两步
1 构造y中的对象,分配内存初始化。
2 用operator =进行赋值。
y分配内存失败就不进行下面的赋值。而operator =是可以保证异常安全的。
但是如果使用拷贝构造函数则不能保证上下文完整性。
高手们还有什么其他的意见可以讨论。