016: push_back()

push_back() を定義します。

std::vector の関数の中でもよく使う関数ではないかと思います。有名な「capacity が足りない場合は、現在の capacity の値を倍にする」という領域確保の戦略も併せて実装します。

template <typename T>
class myvector
{
...

    /**
     * @brief      Appends the given element value to the end of the container.
     *             The new element is initialized as a copy of value.
     *             If the new size() is greater than capacity() then all iterators
     *             and references (including the past-the-end iterator) are invalidated.
     *             Otherwise only the past-the-end iterator is invalidated.
     * @param[in]  value: The value of the element to append.
     * @throw      std::length_error: If new capacity is greater than max_size().
     * @throw      std::bad_alloc: If malloc() (or realloc()) fails to allocate storage.
     */
    void push_back(const value_type& value)
    {
        if (need_twice_capacity()) {
            reallocation(twice_length(), realloc_switcher());
        }
        new(&heap_[size_]) value_type(value);
        size_++;
    }

    /**
     * @brief      Appends the given element value to the end of the container.
     *             The value is moved into the new element.
     *             If the new size() is greater than capacity() then all iterators
     *             and references (including the past-the-end iterator) are invalidated.
     *             Otherwise only the past-the-end iterator is invalidated.
     * @param[in,out] value: The value of the element to append.
     * @throw      std::length_error: If new capacity is greater than max_size().
     * @throw      std::bad_alloc: If malloc() (or realloc()) fails to allocate storage.
     */
    void push_back(value_type&& value)
    {
        if (need_twice_capacity()) {
            reallocation(twice_length(), realloc_switcher());
        }
        new(&heap_[size_]) value_type(std::forward<value_type&&>(value));
        size_++;
    }
...

private:
    /**
     * @brief      Check twice capacity is needed or not.
     * @return     true: Need twice capacity.
     *             false: Not need twice capacity.
     * @throw      std::length_error: If size_ already reaches MAX_SIZE.
     */
    bool need_twice_capacity(void) const
    {
        if (size_ >= MAX_SIZE) {
            throw std::length_error("myvecotr::need_twice_capacity()");
        }
        return (size_ + 1) > capacity_;
    }

    /**
     * @brief      Make twice capacity value.
     * @return     New capacity which is twice as current capacity.
     *             If current capacity is 0, then return 1.
     * @throw      std::length_error: If new capacity is greater than the maximum size.
     */
    size_type twice_length(void) const
    {
        if (capacity_ > (MAX_SIZE / 2)) {
            throw std::length_error("myvecotr::twice_length()");
        }

        return capacity_ ? capacity_ * 2 : 1;
    }
    ...

};
twice_length() という capacity の倍の値を計算する関数を用意します。単純に capacity_ の値を倍にしてから std::length_error のチェックをすると、オーバーフローが発生する可能性があります。そのため、倍にする前に std::length_error のチェックをするようにします。

twice_length() の前には need_twice_capacity() という関数を呼びます。これは、capacity を大きくする必要があるかどうかをチェックする関数です。また、size_ + 1 がオーバーフローする場合の対処も行います。 push_back() では、まず need_twice_capacity() で capacity_ を大きくする必要があるかどうかをチェックします。
capacity を大きくする操作は、以前作成した reallocation() を流用可能です。

領域確保に関する操作が終われば、末尾になる要素に対してコンストラクタを呼び、size を1だけ大きくします。

第二引数が左辺値参照の push_back() と右辺値参照の push_back() の違いは、コンストラクタを呼ぶ時に、コピーコンストラクタが呼ばれるか、ムーブコンストラクタが呼ばれるか、という点のみです。


全ソースコード: https://github.com/suomesta/myvector/tree/master/016

0 件のコメント:

コメントを投稿