STL源码剖析(五)构造和析构的基本工具

STL源码剖析(五)构造和析构的基本工具


在C++中,使用new运算符的时候,会先为对象分配内存,然后再调用其构造函数。STL为了更加灵活, 将内存的分配和对象的构造函数调用分开,前面我们所谈到的空间配置器只负责内存的分配和释放,STL为对象的构造和析构提供了两个全局函数 construct()destroy(),在容器中会使用空间配置器来分配和释放内存,然后调用这两个全局函数来构造和析构对象

一、如何将对象的内存分配释放与构造析构分开?

在看STL的源码前,我们先来看一看,是如何将对象的内存分配与构造析构分开的

关于对象的内存分配与释放,直接使用mallocfree即可,但是mallocfree只会分配内存,并不会调用对象的构造函数和析构函数

先看一下如何调用对象的构造函数

假设我们有一个类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) {}

整一个关系如下所示

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值