先頭へ戻る

アロケータ 解答ページ | Programming Place Plus C++編【標準ライブラリ】 第32章

Programming Place Plus トップページC++編

先頭へ戻る

C++編で扱っている C++ は 2003年に登場した C++03 という、とても古いバージョンのものです。C++ はその後、C++11 -> C++14 -> C++17 -> C++20 と更新されており、今後も 3年ごとに更新されます。
なかでも C++11 での更新は非常に大きなものであり、これから C++ の学習を始めるのなら、C++11 よりも古いバージョンを対象にするべきではありません。特に事情がないなら、新しい C++ を学んでください。 当サイトでは、C++14 をベースにした新C++編を作成中です。

問題①

問題① std::vector のコンストラクタの実装例を挙げました。これを踏まえると、デストラクタはどうなっていると考えられますか? アロケータと関係がないことについては気にしなくて良いです。


外部から指定されたアロケータか、デフォルトのアロケータを使って、オブジェクトの解体と、メモリ領域の解放を行っています。順番に注意してください。

template <typename T, typename Allocator = allocator<T> >
class vector {
public:
    typedef std::size_t  size_type;

    T*         elems;
    Allocator  allocator;

    ~vector()
    {
        if (empty()) {
            return;
        }

        // オブジェクトを解体する
        for (size_type i = 0; i < size(); ++i) {
            allocator.destroy(&elems[i]);
        }

        // メモリ領域を解放する
        allocator.deallocate(elems, 1);
    }
};

問題②

問題② std::list で独自のアロケータを使い、rebind のメカニズムが機能していることを確認してください。


たとえば、int型を確保するアロケータを用意して、直接、メモリ確保などの操作を行った場合と、std::list に同じアロケータを渡した場合の違いを確認します。typeid(【言語解説】第31章)を使って、アロケータの各種メンバ関数で、型名を出力するようにしてみます。

#include <iostream>
#include <limits>
#include <list>
#include <typeinfo>

template <typename T>
class MyAllocator {
public:
    typedef size_t    size_type;
    typedef ptrdiff_t difference_type;
    typedef T*        pointer;
    typedef const T*  const_pointer;
    typedef T&        reference;
    typedef const T&  const_reference;
    typedef T         value_type;

    template <typename U>
    struct rebind {
        typedef MyAllocator<U> other;
    };

    MyAllocator() throw() {}
    MyAllocator(const MyAllocator& rhs) throw() {}
    template <typename U> MyAllocator(const MyAllocator<U>& rhs) throw() {}

    ~MyAllocator() throw() {}

    pointer allocate(size_type num, std::allocator<void>::const_pointer hint = 0)
    {
        std::cout << "allocate: " << typeid(T).name() << std::endl;

        const std::size_t size = num * sizeof(T);
        return static_cast<pointer>(::operator new(size));
    }

    void deallocate(pointer p, size_type num)
    {
        std::cout << "deallocate: " << typeid(T).name() << std::endl;

        ::operator delete(p);
    }

    void construct(pointer p, const T& value)
    {
        new((void*)p) T(value);
    }

    void destroy(pointer p)
    {
        ((T*)p)->~T();
    }

    pointer address(reference value) const { return &value; }
    const_pointer address(const_reference value) const { return &value; }

    size_type max_size() const throw()
    {
        return std::numeric_limits<size_t>::max() / sizeof(T);
    }
};

template <>
class MyAllocator<void> {
public:
    typedef void*        pointer;
    typedef const void*  const_pointer;
    typedef void         value_type;

    template <typename U>
    struct rebind {
        typedef MyAllocator<U> other;
    };
};

template <typename T1, typename T2>
bool operator==(const MyAllocator<T1>&, const MyAllocator<T2>&) throw()
{
    return true;
}

template <typename T1, typename T2>
bool operator!=(const MyAllocator<T1>&, const MyAllocator<T2>&) throw()
{
    return false;
}


int main()
{
    typedef MyAllocator<int> IntAllocator;
    typedef std::list<int, IntAllocator> IntAllocatorList;

    IntAllocator myAllocator;

    int* p = myAllocator.allocate(1);
    myAllocator.construct(p, 0);
    myAllocator.destroy(p);
    myAllocator.deallocate(p, 1);

    IntAllocatorList lst(myAllocator);
    lst.push_back(0);
}

実行結果(Visual Studio 2017):

allocate: int
deallocate: int
allocate: struct std::_List_node<int,void * __ptr64>
allocate: struct std::_Container_proxy
allocate: struct std::_List_node<int,void * __ptr64>
deallocate: struct std::_List_node<int,void * __ptr64>
deallocate: struct std::_List_node<int,void * __ptr64>
deallocate: struct std::_Container_proxy

実行結果(clang 5.0.0):

allocate: i
deallocate: i
allocate: NSt3__111__list_nodeIiPvEE
deallocate: NSt3__111__list_nodeIiPvEE

std::list の場合には、複雑な型名が出力されていますが、よく見ると「list node」のような名前が見えます。これが、内部的に持っている、管理用の構造体の型だと推察できます(実際にソースコードを確認すれば見つかります)。


参考リンク


更新履歴

’2019/2/12 VisualStudio 2015 の対応終了。

’2018/4/2 「VisualC++」という表現を「VisualStudio」に統一。

’2018/1/5 Xcode 8.3.3 を clang 5.0.0 に置き換え。

’2017/7/30 clang 3.7 (Xcode 7.3) を、Xcode 8.3.3 に置き換え。

’2017/2/25 新規作成。



第32章のメインページへ

C++編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
Twitter でツイート Twitter をフォロー LINE で送る
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー