一、前言

  上一篇,我先完成了对内存配置器的实现。然而后面在内存上的算法还依赖于两个全局函数,construct()和destroy(),前者负责在指定的内存上调用对象的构造函数,在内存上构造出对象。后者则是相反,在指定内存上调用对象的析构函数,销毁对象。(注意:这两个函数不涉及对象内存的分配和释放,对象构造在指定的已分配好的内存上,析构也只是销毁对象,对于对象占用的那块内存,没有释放,如需释放,还需自己去free)。

二、全局construct()函数简介

  construct(),主要功能前面已经说明,其在指定内存上构造对象。在指定内存上构造对象的功能,我们一般使用placement new。这里也是如此。construct()函数实现比较简单。实现如下:

 // construct()
// 接受一个已分配好的内存,在其上以value为构造函数参数的来构造一个T1对象
template <class T1, class T2>
inline void construct(T1* p, const T2& value)
{
new (p) T1(value); //placement new; 调用T1::T1(value);
}

三、全局的destroy()函数简介

  destroy()函数实现就比construct()复杂一点。这里利用了对象类型的类型属性的不同进行重载,对于不同对象的destroy都进行最适当的处理。我先把destroy()的泛化到特化的过程展示如下:

图1:destroy()和construct()示意

1.destroy()的第一个版本

  第一个版本只接受一个对象指针,其是对于单个对象的析构。很简单,只是调用对象本身的析构函数即可。

 // destroy() 第一个版本
// destroy接受一个对象指针,调用该对象的析构函数
// 注意:destroy函数只析构对象,没有释放其内存
template <class T>
inline void destroy(T* pointer)
{
pointer->~T(); //调用dtor ~T()
}

2.destroy()的第二版本

  对于destroy()的第二版本,他接受两个迭代器标识的一段区间,对该区间内所有对象进行析构。  

  第二版本,首先destroy()对于外部的有一个统一接口destroy()模版函数:

 template <class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIter last)
{
// 利用value_type()函数提取处迭代器所指向的类型,
// 并用其类型产生一个临时对象,利用编译器的类型推导,
// 获取迭代器指向类型,以便后面萃取其相关属性
__destroy(first, last, value_type(first));
}

  其中value_type()也是一个函数模版,其主要作用是,利用编译器的类型推导,得到迭代器的类型,再利用迭代器的属性萃取类型,得到迭代器的内嵌类型value_type。并利用编译器的类型推导,在__destroy()函数中取得迭代器的value_type内嵌类型。(注:在STL的迭代器都有五个内嵌类型,其中value_Type,用于表示迭代器的指向对象的类型。同时,还有一个全局的函数value_type(),用于提取出迭代器的value_type,并用该类型生成一个该类型的指针)

 template <class Iterator>
inline typename iterator_traits<Iterator>::value_type*
value_type(const Iterator&)
{
// 制造一个指针类型,比制造对象用于重载效率要高
return static_cast<typename iterator_traits<Iterator>::value_type*>();
}

  由destroy()---->__destroy()主要为了利用编译器的类型推导功能,获取迭代器的指向对象的类型value_type

 template <class ForwardIterator, class T>
inline void __destroy(ForwardIterator first, ForwardIterator last, T*)
{
// 利用__type_traits萃取处T类型的析构析构函数的否是trivial,
// 并利用其has_trivial_destructor的定义类型,产生一个临时对象
// 利用其进行重载
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
return __destroy_aux(first, last, trivial_destructor());
}

  在__destroy()函数中,又利用了用来类型属性萃取的类型__type_traits,萃取迭代器指向对象类型的一些属性。在这里,就是萃取迭代器指向对象类型对于是否有trivial析构函数的定义,并利用该定义的类型生成临时对象用于重载。

  对于has_trivial_destructor类型的具体定义的类型为__false_type(__false_type和__true_type都是类型,他们是标签类型,主要是表示一个属性是否为真。使用标签类型,主要是为了能够利用类型进行重载,这样就可以对于不同属性的结果进行不同的处理),那么说明迭代器指向类型的析构函数是non-trivail的,那么需要调用对象的析构函数。

 // dtor 是 non-trivial的,必须逐个调用析构函数析构
template <class ForwardIterator>
inline void
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type)
{
for (; first < last; ++first)
destory(&*first); //调用第一个版本,以调用析构函数
}

  对于has_trivial_destructor类型的具体定义的类型为__true_type,那么迭代器指向对象类型的析构函数,其实是无用的,那么就可以不用调用了。

 // dtor 是trivial的,不用调用析构函数
template <class ForwardIterator>
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}

  最后,对于char*和wchar_t*类型的迭代器,由于char和wchar_t其都是内置类型,他们没有析构函数,也可以认为其析构是trivial的,那么就不用调用析构函数了。由于,如果采用上面的版本,还需要进行__destroy()和__destroy_aux(),这样的调用其实是无用的,因为最后什么都没有执行,为了效率的考虑,直接对第二版的destroy()函数模版进行特化。

// 针对char*的特化版本

// 对于迭代器类型是char*的,由于其char类型利用__type_traits
// 萃取出,其应用调用__destroy_aux(ForwardIterator, ForwardIterator, __true_type)
// 但是函数其实其实什么都没有执行,却在调用了函数,并可能产生几个临时对象,
// 所以针对char*类型的迭代器进行特化,避免上述情况出现,提高效率
inline void destroy(char *, char *) {} // 针对wchar_t*的特化版本
// 原理同上
inline void destory(wchar_t*, wchar_t*) {}

四、stl_construct.h的完整实现源码

  如下:

 /*************************************************************************
> File Name: stl_construct_wjzh.h
> Author: wjzh
> Mail: wangjzh_1@163.com
> Created Time: 2014年11月06日 星期四 14时35分29秒
************************************************************************/ // 该文件中提供构造和析构的基本工具 #ifndef __SGI_STL_INTERNAL_CONSTRUCT_WJZH_H
#define __SGI_STL_INTERNAL_CONSTRUCT_WJZH_H #include <new.h> __STL_BEGIN_NAMESPACE // destroy() 第一个版本
// destroy接受一个对象指针,调用该对象的析构函数
// 注意:destroy函数只析构对象,没有释放其内存
template <class T>
inline void destroy(T* pointer)
{
pointer->~T(); //调用dtor ~T()
} // construct()
// 接受一个已分配好的内存,在其上以value为构造函数参数的来构造一个T1对象
template <class T1, class T2>
inline void construct(T1* p, const T2& value)
{
new (p) T1(value); //placement new; 调用T1::T1(value);
} // destory(第二个版本实现)
// 接受两个迭代器,在两个迭代器标示的区间内逐个以最适当的方式析构
// 迭代器所指向的对象 // dtor 是 non-trivial的,必须逐个调用析构函数析构
template <class ForwardIterator>
inline void
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type)
{
for (; first < last; ++first)
destory(&*first); //调用第一个版本,以调用析构函数
} // dtor 是trivial的,不用调用析构函数
template <class ForwardIterator>
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {} template <class ForwardIterator, class T>
inline void __destroy(ForwardIterator first, ForwardIterator last, T*)
{
// 利用__type_traits萃取处T类型的析构析构函数的否是trivial,
// 并利用其has_trivial_destructor的定义类型,产生一个临时对象
// 利用其进行重载
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
return __destroy_aux(first, last, trivial_destructor());
} template <class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIter last)
{
// 利用value_type()函数提取处迭代器所指向的类型,
// 并用其类型产生一个临时对象,利用编译器的类型推导,
// 获取迭代器指向类型,以便后面萃取其相关属性
__destroy(first, last, value_type(first));
} // 针对char*的特化版本 // 对于迭代器类型是char*的,由于其char类型利用__type_traits
// 萃取出,其应用调用__destroy_aux(ForwardIterator, ForwardIterator, __true_type)
// 但是函数其实其实什么都没有执行,却在调用了函数,并可能产生几个临时对象,
// 所以针对char*类型的迭代器进行特化,避免上述情况出现,提高效率
inline void destroy(char *, char *) {} // 针对wchar_t*的特化版本
// 原理同上
inline void destory(wchar_t*, wchar_t*) {} __STL_END_NAMESPACE #endif /* __SGI_STL_INTERNAL_CONSTRUCT_WJZH_H */ // End

自己动手实现STL 02:构造析构的基本工具construct()和destroy()(stl_construct.h)的更多相关文章

  1. 自己动手实现STL 03:内存基本处理工具(stl_uninitialized.h)

    一.前言 前面两篇已经编写了内存配置器和建构解构工具函数.这里,就准备编写并介绍下内存基本处理工具函数.比如uninitialized_copy().uninitialized_copy和 unini ...

  2. Effective C++ —— 构造/析构/赋值运算(二)

    条款05 : 了解C++默默编写并调用哪些函数 编译器可以暗自为class创建default构造函数.copy构造函数.copy assignment操作符,以及析构函数. 1. default构造函 ...

  3. 《Effective C++》第2章 构造/析构/赋值运算(1)-读书笔记

    章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...

  4. C++ map.insert 传参类型不同,构造/析构次数不同

    1. 传参方式 使用 insert 为 map 插值时,insert 的传参包含以下几种可能: make_pair 生成对象 pair(key_type, value_type) 生成对象 pair( ...

  5. EffectiveC++ 第2章 构造/析构/赋值运算

    我根据自己的理解,对原文的精华部分进行了提炼,并在一些难以理解的地方加上了自己的"可能比较准确"的「翻译」. Chapter 2 构造 / 析构 / 赋值 条款 05:了解C++ ...

  6. 《Effective C++》第2章 构造/析构/赋值运算(2)-读书笔记

    章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...

  7. c++继承构造析构调用原则以及特殊变量处理

    一.继承中的构造析构调用原则 1.子类对象在创建时会首先调用父类的构造函数 2.父类构造函数执行结束后,执行子类构造函数 3.当父类构造函数有参数时,需要在子类的初始化列表中显示调用: 4.析构函数调 ...

  8. 自己动手实现STL 01:内存配置器的实现(stl_alloc.h)

    一.前言 在STL中,容器是其中的重中之重,基本的STL中的算法,仿函数等都是围绕着容器实现的功能.而,内存配置器,是容器的实现的基础.所以,我第一次要去编写便是内存配置器的实现.在STL中,内存配置 ...

  9. HexoC++第04课 构造析构.md

    C++第04课 构造析构.mdhtml {overflow-x: initial !important;}#write, body { height: auto; } #write, #write h ...

随机推荐

  1. SQL_SERVER_2008升级SQL_SERVER_2008_R2办法 (一、升级;二、重新xie载安装)

    SQL_SERVER_2008升级SQL_SERVER_2008_R2两种办法   今天将由于需要就将我的SQL 2008升级到SQL 2008 R2. 说到为什么要升级是因为,因附加数据库时发现报错 ...

  2. 谈谈php中上传文件的处理

    这是一个表单的时代... 我们在浏览器中编辑自己的信息,会遇到上传头像:在文库中,我们会上传文档......到处存在“上传”这个词. php是最好的语言(其他语言的程序猿们不要打我...).php在处 ...

  3. 给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。

    // test14.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> #include< ...

  4. text-align:-moz-center与text-align:-webkit-center区别与用法

    最近发现各浏览器的不兼容,关于text-align:center这个很多浏览器不兼容. 1.测试发现:text-align:center在IE下是管用的. 2.text-align:-moz-cent ...

  5. 关于Backtracing中有重复元素的处理办法

    backtracing是一个常用的解法.之前遇到一个题目,求一个集合的子集, 例如给定{1,2,3,4,5},求其大小为3的子集. 利用backtracing可以较快的给出答案. 然而,该题还有一个变 ...

  6. Appium环境配置

    一.JDK下载.安装及其环境配置 1.下载.安装略过…… 2.环境配置,以jdk-8u45为例,默认安装在 C:\Program Files\Java\jdk1.8.0_45\路径下. 下面设置环境变 ...

  7. POJ2299Ultra-QuickSort

    http://poj.org/problem?id=2299 题意 : 排序,求排序次数,本来以为用冒泡可以搞定,事实上,那么大的数据以及一个TLE告诉我,会超时......... 思路 : 问了一下 ...

  8. [Hibernate]dynamic-insert和dynamic-update属性

    这二个属性默认情况均为false,你可以通过以下二种方式进行配置使用: 1.Annotation @Entity @Table(name = "stock_transaction" ...

  9. [转]数据结构之Trie树

    1. 概述 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树. Trie一词来自retrieve,发音为/tr ...

  10. net中使用母版页

    .net中使用母版页的优点 母版页提供了开发人员已通过传统方式创建的功能,这些传统方式包括重复复制现有代码.文本和控件元素:使用框架集:对通用元素使用包含文件:使用 ASP.NET 用户控件等.母版页 ...