STL源码剖析(五)构造和析构的基本工具
在C++中,使用new运算符的时候,会先为对象分配内存,然后再调用其构造函数。STL为了更加灵活,
将内存的分配和对象的构造函数调用分开
,前面我们所谈到的空间配置器只负责内存的分配和释放,STL为对象的构造和析构提供了两个全局函数
construct()
、
destroy()
,在容器中会使用空间配置器来分配和释放内存,然后调用这两个全局函数来构造和析构对象
一、如何将对象的内存分配释放与构造析构分开?
在看STL的源码前,我们先来看一看,是如何将对象的内存分配与构造析构分开的
关于对象的内存分配与释放,直接使用malloc
和free
即可,但是malloc
和free
只会分配内存,并不会调用对象的构造函数和析构函数
先看一下如何调用对象的构造函数
假设我们有一个类A
class A
{
public:
A()
{
std::cout<<"A construct"<<std::endl;
}
~A()
{
std::cout<<"A destroy"<<std::endl;
}
};
首先我们分配一个A对象的内存
A* a = malloc(sizeof(A));
我们无法向调用成员函数的方法去调用A的构造函数,这个语法是C++不允许的
a->A(); //C++不允许
C++提供了一个操作符,叫做placement new
,其语法如下
new(T*) construct();
如我们要调用a的构造函数,可以这么做
new(a) A();
那么调用析构函数应该怎么做呢?
调用析构函数就简单多了,只需要像调用成员函数一样就行,如下
a->~A();
完整的程序如下,你可以试着自己运行以下
#include <iostream>
#include <stdlib.h>
class A
{
public:
A()
{
std::cout<<"A construct"<<std::endl;
}
~A()
{
std::cout<<"A destroy"<<std::endl;
}
};
int main(int argc, char* argv[])
{
/* 分配内存 */
A* a = (A*)malloc(sizeof(A));
/* 调用构造函数 */
new(a) A();
/* 调用析构函数 */
a->~A();
/* 释放内存 */
free(a);
return 0;
}
二、construct
construct较为简单,它是一个函数模板,直接调用placement new
template <class T1, class T2>
inline void construct(T1* p, const T2& value) {
new (p) T1(value);
}
三、destroy
destroy比较复杂, destroy实现了两个版本,第一个版本参数为一个指针,这个实现就很简单了,只是调用的析构函数,如下
template <class T>
inline void destroy(T* pointer) {
pointer->~T();
}
第二个版本较为复杂,它接收两个迭代器,指示要析构对象的范围,其实现如下
template <class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last) {
__destroy(first, last, value_type(first));
}
此外还实现了另外两个特化版本
inline void destroy(char*, char*) {}
inline void destroy(wchar_t*, wchar_t*) {}
我们再来看一下__destroy(first, last, value_type(first))
调用,其中value_type
是获取迭代器所指向对象的类型,这一部分是通过traits
机制实现,这部分我们以后会再具体讲解
__destroy
的实现如下
template <class ForwardIterator, class T>
inline void __destroy(ForwardIterator first, ForwardIterator last, T*) {
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
__destroy_aux(first, last, trivial_destructor());
}
其中通过trivial_destructor()
获取此对象是否不需要被析构,它是通过traits
机制获取的,我们之后会讲解,这里暂且知道其作用就足够了
根据trivial_destructor
返回类型的不同,定义了两个版本的__destroy_aux
,一个是对象需要被析构,一个是对象不需要被析构,如下
template <class ForwardIterator>
inline void
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {
for ( ; first < last; ++first)
destroy(&*first);
}
template <class ForwardIterator>
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}
整一个关系如下所示