C++STL-コンテナ空間コンフィギュレータallocatorの原理

22175 ワード

知識の学習は点滴記録にあり、たゆまぬ努力を続けている.知識の学習には深さと広さが必要で、表面だけに流れてはいけない.知識は総括するのが上手で、理解できるだけではなくて、更にどのように表現することを知っています!
目次
  • 簡単なvector容器
  • を実現する
  • 容器が直面する問題
  • スペースコンフィギュレータ紹介
  • は、スペースコンフィギュレータ付きvector容器
  • を実現する.
    簡単なvectorコンテナを実現
    C++STLすべてのコンテナの実装は1つの空間コンフィギュレーションallocatorに依存する必要があります.私たちは普段コンテナを使用するときは注意していませんが、私たちはずっとそれを使用しています.C++STLライブラリはデフォルトの空間コンフィギュレーションallocatorの実装を提供しています.比較的簡単です.もちろん、空間コンフィギュレーションの原理を深く理解する必要があります.その後、カスタムallocatorを提供することができます.
    まず、簡単なvectorコンテナの実装を見てみましょう.コードは以下の通りです.
    #include 
    using namespace std;
    /*
                 ,     vector      ,
                         ,      
         
    */
    template<typename T>
    class Vector
    {
    public:
    	//     
    	Vector(int size = 0)
    		:mcur(0), msize(size)
    	{
    		mpvec = new T[msize];
    	}
    	//     
    	~Vector()
    	{
    		delete[]mpvec;
    		mpvec = nullptr;
    	}
    	//       
    	Vector(const Vector<T> &src)
    		:mcur(src.mcur), msize(src.msize)
    	{
    		mpvec = new T[msize];
    		for (int i = 0; i < msize; ++i)
    		{
    			mpvec[i] = src.mpvec[i];
    		}
    	}
    	//       
    	Vector<T>& operator=(const Vector<T> &src)
    	{
    		if (this == &src)
    			return *this;
    
    		delete []mpvec;
    
    		mcur = src.mcur;
    		msize = src.msize;
    		mpvec = new T[msize];
    		for (int i = 0; i < msize; ++i)
    		{
    			mpvec[i] = src.mpvec[i];
    		}
    		return *this;
    	}
    	//         
    	void push_back(const T &val)
    	{
    		if (mcur == msize)
    			resize();
    		mpvec[mcur++] = val;
    	}
    	//         
    	void pop_back()
    	{
    		if (mcur == 0)
    			return;
    		--mcur;
    	}
    private:
    	T *mpvec; //     ,       
    	int mcur; //            
    	int msize; //            
    
    	//   2     
    	void resize()
    	{
    	    /*     vector  ,      0-1-2-4-8-16-32-... 2   
    		     ,  vector              ,    reserve
    		               。*/
    		if (msize == 0)
    		{
    			mpvec = new T[1];
    			mcur = 0;
    			msize = 1;
    		}
    		else
    		{
    			T *ptmp = new T[2 * msize];
    			for (int i = 0; i < msize; ++i)
    			{
    				ptmp[i] = mpvec[i];
    			}
    			delete[]mpvec;
    			mpvec = ptmp;
    			msize *= 2;
    		}
    	}
    };
    

    上記は簡単なvectorコンテナのコード実装であり、以下のコードで上記のVectorを使用します.
    //         A
    class A
    {
    public:
    	A() { cout << "A()" << endl; }
    	~A() { cout << "~A()" << endl; }
    };
    int main()
    {
    	Vector<A> vec(10); // 10           ,      10 A  
    	A a1, a2, a3;
    	cout << "---------------" << endl;
    	vec.push_back(a1);
    	vec.push_back(a2);
    	vec.push_back(a3);
    	cout << "---------------" << endl;
    	vec.pop_back(); //   a3         
    	cout << "---------------" << endl;
    
    	// vec     ,    2    A  ,      10 
    	return 0;
    }
    

    上のコードを実行して、印刷結果は以下の通りです:A()A()A()A()A()A()A()A()/上からこれがvector容器の中で、10個のオブジェクトA()/ここから下の3個のA構造関数を構築しました.a 1,a 2,a 3の3個のオブジェクトA()A()+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//ここで問題があります.vec.pop_back()は末尾Aオブジェクトを削除するが,析出呼び出しは行われず,資源漏洩をもたらす可能性がある~A()~A()/上からここまでの3つの析出関数を解析し,a 1,a 2,a 3の3つのオブジェクト~A()~A()~A()~A()~A()~A()~A()~A()/上からここまでの10の析出関数を解析し,vector容器中のオブジェクトをすべて解析する.
    コンテナが直面する問題
    上記のコードには、次の3つの問題があります.
  • コンテナを定義するときVectorvec(10)希望能够收容10个要素的空间开放在下层。必须建立10个A办公室.此时,我不打算在便利店追加数据。这样的结构关数的呼出,纯粹是浪费效率的。
  • 从康特纳删除要素的时候vec.pop_back()とは、コンテナーの末尾にあるオフサイトAを削除することを意味するが、Aオフサイトの構造相关数が呼び出されず、Aオフサイトが外部リソスを占有している場合、リソスの解放コードは必ずAの構造相关数の中にあり、リソスの漏洩の問題がある。vec康特纳在解析相关数的角色时,没有解析有有效的A办公室。实际上上,最终的vec控制器中,只有我们进入的有效的办公室a 1和a 2有2个。a 3只有删除的2次解析就行了,但10次解析不合理。关于这三个问题,我们的解决方案就是这样的。定义康特纳的时候,只申请梅莫利,没必要建立那样的无效的办公室.在搭档中追加要素的时候,在应对的位置再次构筑奥布杰特就行啦。如果从康特纳删除要素的话,不仅仅是mcur,而呼吁删除橄榄球的结构关数,需要解放橄榄球占有的外部リソス.康特纳vec出现的时候,必须出现康特纳内的有效的办公室,解放康特纳总体的梅莫里苏斯.基于以上的问题解决方法,我们持有这样的需求.分为奥布杰克的梅莫里昂和控制相关的2个程序。分成奥布杰特专业文件和梅莫利解放的2个专业文件。因此,new和delete不能直接使用康特纳.新不仅开存档,可以自动呼吁构造关数构筑办公室。delete,从专业网络开放梅莫利。那么,上面的需求塔斯克是谁完成的?这是今天介绍的这个便利店的特别节目便利店allocator.西班斯康菲格雷塔的说明西班斯·康菲格雷塔的核心功能分解了奥布杰克的梅莫里的开发和奥布杰克的构造的程序,分解奥布杰克的程序和梅莫里的解放程序.因此,西班斯·康菲格雷塔主要提供以下4个关数.我提供C++STL橄榄球类似的空间便利店的实装,在负责专业计算机的相关机能allocate化妆品的开发后,将由负责deallocate化妆品的解放而在construct计算机内构筑平板检查的destroy解析对象。カスタムスペースコンフィギュレータ template<typename T> struct myallocator { //メモリスペースの開拓 T* allocate(size_t size) { return (T*)::operator new(sizeof(T)*size);// malloc割り当てメモリに相当 } //メモリ容量の解放 void deallocate(void *ptr, size_t size) { ::operator delete(ptr, sizeof(T)*size);// freeリリースメモリに相当 } //担当者構成 void construct(T *ptr, const T &val) { new ((void*)ptr) T(val);// 位置決めnewで指定したメモリにオブジェクトを構築する } //担当者分析 void destroy(T *ptr) { ptr->~T();// 呼び出しオブジェクトの構造関数を表示 } }; 在上面实现的空间环节比较简单,梅莫里管理仍然使用operator new和operator delete,实际上在malloc和free的梅莫里管理中,当然allocator追加了1个梅莫里普尔的实现,相当于是在那里加油管理方式.可以看C++STL演唱会vector contena的克拉斯汀定义赫达.科德如下。template<class _Ty, class _Alloc = allocator<_Ty>> class vector 从上面的vector康特纳克拉斯汀普雷特定义来看,表示了2个电梯型电脑.Ty是被搭档的数据的类型。Alloc是西班斯康时装的类型,如果用户不加热尺寸的话,使用了莱布拉利内的デフォルト的allocator.这与上记的特别节目合作的实装相似。我们将在我们实现的空间便利店allocator上追加,补正我们最先提供的西班斯康芬芬时装的vector代码。include using namespace std; //カスタムスペースコンフィギュレータ template<typename T> struct myallocator { //メモリスペースの開拓 T* allocate(size_t size) { return (T*)::operator new(sizeof(T)*size);// malloc割り当てメモリに相当 } //メモリ容量の解放 void deallocate(void *ptr, size_t size) { ::operator delete(ptr, sizeof(T)*size);// freeリリースメモリに相当 } //担当者構成 void construct(T *ptr, const T &val) { new ((void*)ptr) T(val);// 位置決めnewで指定したメモリにオブジェクトを構築する } //担当者分析 void destroy(T *ptr) { ptr->~T();// 呼び出しオブジェクトの構造関数を表示 } }; /* Vectorコンテナのインプリメンテーションにスペースコンフィギュレータallocatorを追加 */ template<typename T, typename allocator = myallocator<T>> class Vector { public: //コンストラクション関数、カスタムスペースプロファイルに転送できます。そうしないと、デフォルトのallocatorを使用します。 Vector(int size = 0, const allocator &alloc = allocator()) :mcur(0), msize(size), mallocator(alloc) { //容器の下部スペースのみを開き、オブジェクトを構築しない mpvec = mallocator.allocate(msize); } //解析関数 ~Vector() { //先析コンテナ内のオブジェクト for (int i = 0; i < mcur; ++i) { mallocator.destroy(mpvec+i); } //容器使用ヒープメモリの解放 mallocator.deallocate(mpvec, msize); mpvec = nullptr; } //コピーコンストラクタ Vector(const Vector<T> &src) :mcur(src.mcur) , msize(src.msize) , mallocator(src.mallocator) { //容器の下部スペースのみを開き、オブジェクトを構築しない mpvec = mallocator.allocate(msize); for (int i = 0; i < mcur; ++i) { //指定アドレスmpvec+iにsrcという値を設定.mpvec[i]のオブジェクト mallocator.construct(mpvec+i, src.mpvec[i]); } } //代入リロード関数 Vector<T> operator=(const Vector<T> &src) { if (this == &src) return *this; //先析コンテナ内のオブジェクト for (int i = 0; i < mcur; ++i) { mallocator.destroy(mpvec + i); } //容器使用ヒープメモリの解放 mallocator.deallocate(mpvec, msize); mcur = src.mcur; msize = src.msize; //容器の下部スペースのみを開き、オブジェクトを構築しない mpvec = mallocator.allocate(msize); for (int i = 0; i < mcur; ++i) { //指定アドレスmpvec+iにsrcという値を設定.mpvec[i]のオブジェクト mallocator.construct(mpvec + i, src.mpvec[i]); } return *this; } //末尾挿入データ関数 void push_back(const T &val) { if (mcur == msize) resize(); mallocator.construct(mpvec + mcur, val); mcur++; } //末尾削除データ関数 void pop_back() { if (mcur == 0) return; --mcur; //削除されたオブジェクトのプロファイル mallocator.destroy(mpvec + mcur); } private: T *mpvec; // ダイナミック配列、コンテナの要素の保存 int mcur; // 現在有効な要素を保存する個数 int msize; // 保存容器拡張後の全長 allocator mallocator; // コンテナのスペースコンフィギュレータオブジェクトの定義 //容器2倍拡張関数 void resize() { if (msize == 0) { mpvec = mallocator.allocate(sizeof(T)); mcur = 0; msize = 1; } else { T *ptmp = mallocator.allocate(2 * msize); for (int i = 0; i < msize; ++i) { mallocator.construct(ptmp + i, mpvec[i]); } //先析コンテナ内のオブジェクト for (int i = 0; i < msize; ++i) { mallocator.destroy(mpvec + i); } //容器使用ヒープメモリの解放 mallocator.deallocate(mpvec, msize); mpvec = ptmp; msize *= 2; } } }; //簡単なテストクラスA class A { public: A() { cout << "A()" << endl; } ~A() { cout << "~A()" << endl; } }; int main() { Vector<A> vec(10); // ここではメモリのみが開き、オブジェクトは構築されません。 A a1, a2, a3; cout << "+++++++++++++++" << endl; vec.push_back(a1); vec.push_back(a2); vec.push_back(a3); cout << "+++++++++++++++" << endl; vec.pop_back(); // a 3を削除してa 3オブジェクトをプロファイルする cout << "+++++++++++++++" << endl; //vec容器解析の場合、内部に有効なA対象が2個しかなく、2回解析して、正しい return 0; } コードの実際の印刷は以下の通りである:A()A()A()/上の3つはA a 1,a 2,a 3である;3つのスタック上のObjectの印刷+++++++++++++++++++++++++++++++++++++++++++++++++++++++++A()/这里是vec.pop_back()是a 3 Object+++++++++++++~A()~A()/上的3个是A a 1,a 2,a 3的解析的3件Object的Profile呼吁~A()/上的2件是vec conteナ内的2件A Object的Profile印刷,根据第一个实装的conteナ,这3个问题表示了。
  • 定义康丁纳的时候Vectorvec(10)希望能够收容10个要素的空间开放在下层。必须建立10个A办公室.此时,我不打算在便利店追加数据。这样的结构关数的呼出,纯粹是浪费效率的。
  • 从康特纳删除要素的时候vec.pop_back()とは、コンテナーの末尾にあるオフサイトAを削除することを意味するが、Aオフサイトの構造相关数が呼び出されず、Aオフサイトが外部リソスを占有している場合、リソスの解放コードは必ずAの構造相关数の中にあり、リソスの漏洩の問題がある。vec康特纳在解析相关数的角色时,没有解析有有效的A办公室。实际上上,最终的vec控制器中,只有我们进入的有效的办公室a 1和a 2有2个。a 3只有删除的2次解析就行了,但10次解析不合理。因此,我们将通过特别节目控制allocator解决,仔细比较最初的Vector和修正后的特别节目控制器的Vector的代码,体现了在便利店的allocator的具体使用。【扩张】SGI STL是标准STL的另一个实装版本,比较广泛使用,内藏了1级和2级的空间构成器的实装,2级的空间构成器可以参照梅莫里普尔的实装。