c++ vector拷贝构造_Urho3D源码剖析-Vector

b8f53ffdb9706936df3357f03b87ecb1.png

Urho3D自己实现了一套常用的容器类型,包括Vector, List, HashMap及其相关类型。 容器类代码全部分布在SourceUrho3DContainer下, 这里的代码可以作为我们分析学习Orho3D源码入手的切入点。

VectorBase

class URHO3D_API VectorBase
{
public:
    /// Construct.
    VectorBase() noexcept :
        size_(0),
        capacity_(0),
        buffer_(nullptr)
    {
    }

    /// Swap with another vector.
    void Swap(VectorBase& rhs)
    {
        Urho3D::Swap(size_, rhs.size_);
        Urho3D::Swap(capacity_, rhs.capacity_);
        Urho3D::Swap(buffer_, rhs.buffer_);
    }

protected:
    static unsigned char* AllocateBuffer(unsigned size);

    /// Size of vector.
    unsigned size_;
    /// Buffer capacity.
    unsigned capacity_;
    /// Buffer.
    unsigned char* buffer_;
};

size_ 用来记录Vector中元素的个数, capacity_ 表示Vector可用容量 buffer_ Vector存放数据的内存地址

VectorBase提供的内部Swap函数会交换着三个成员变量的值。 Urho提供了全局的Swap模板函数,并且对一些类型提供了特化操作

template <class T> inline void Swap(T& first, T& second)
{
    T temp = first;
    first = second;
    second = temp;
}

template <> void Swap<ListBase>(ListBase& first, ListBase& second)
{
    first.Swap(second);
}

Vector

Vector类相关的代码文件为:Vector.{h, cpp},继承了VectorBase

template <class T> class Vector : public VectorBase

Vector提供了与std::vector相似的方法,这里主要分析其中几个重要的成员函数的实现。

/// Resize the vector.
void Resize(unsigned newSize) { DoResize(newSize); }

/// Resize the vector and fill new elements with default value.
void Resize(unsigned newSize, const T& value)
{
    unsigned oldSize = Size();
    DoResize(newSize);
    for (unsigned i = oldSize; i < newSize; ++i)
        At(i) = value;
}
/// Resize the vector and create/remove new elements as necessary.
void DoResize(unsigned newSize)
{
    // If size shrinks, destruct the removed elements
    if (newSize < size_)
        DestructElements(Buffer() + newSize, size_ - newSize);
    else
    {
        // Allocate new buffer if necessary and copy the current elements
        if (newSize > capacity_)
        {
            T* src = Buffer();

            // Reallocate vector
            Vector<T> newVector;
            newVector.Reserve(CalculateCapacity(newSize, capacity_));
            newVector.size_ = size_;
            T* dest = newVector.Buffer();

            // Move old elements
            ConstructElements(dest, src, src + size_, MoveTag{});

            Swap(newVector);
        }

        // Initialize the new elements
        ConstructElements(Buffer() + size_, newSize - size_);
    }

    size_ = newSize;
}

这个方法用来动态调整Vector大小,Resize函数内部会转到DoResize函数来做真正的逻辑。 在DoResize中,当传入的参数newSize小于当前Vector的size时,会将多余的元素进行销毁(DestrutcElements); 当newSize的大小超过了Vector的容量(capacity_)时,会触发新的内存分配,并且会将原来的元素搬移(MoveTag)到新分配的内存中(ConstructElements), 当newSize大于现在Vector的size_, 而又没有超过capacity_时,会在原有Vector元素后面直接构造新的元素。

/// Call the elements' destructors.
static void DestructElements(T* dest, unsigned count)
{
    while (count--)
    {
        dest->~T();
        ++dest;
    }
}

DesctructElements是Vector的static成员函数,会在dest指定的内存位置处,调用count次元素T的析构函数。

static unsigned CalculateCapacity(unsigned size, unsigned capacity)
{
    if (!capacity)
        return size;
    else
    {
        while (capacity < size)
            capacity += (capacity + 1) >> 1;
        return capacity;
    }
}

静态成员函数CalculateCapacity会根据现有的capcity进行扩张(capacity += (capacity + 1) >> 1)来找到满足size大小的一个capacity的值。

static void ConstructElements(T* dest, unsigned count)
{
    for (unsigned i = 0; i < count; ++i)
        new(dest + i) T();
}

/// Copy-construct elements.
template <class RandomIteratorT>
static void ConstructElements(T* dest, RandomIteratorT start, RandomIteratorT end, CopyTag)
{
    const unsigned count = end - start;
    for (unsigned i = 0; i < count; ++i)
        new(dest + i) T(*(start + i));
}

/// Move-construct elements.
template <class RandomIteratorT>
static void ConstructElements(T* dest, RandomIteratorT start, RandomIteratorT end, MoveTag)
{
    const unsigned count = end - start;
    for (unsigned i = 0; i < count; ++i)
        new(dest + i) T(std::move(*(start + i)));
}

ConstructElements的三种重载。 第一种是没有Tag参数的,只是在dest指向的内存处执行了元素T的默认构造函数; 后面两种根据传入的Tag类型分别调用的了copy和move构造函数。

void Reserve(unsigned newCapacity)
{
    if (newCapacity < size_)
        newCapacity = size_;

    if (newCapacity != capacity_)
    {
        T* newBuffer = nullptr;
        capacity_ = newCapacity;

        if (capacity_)
        {
            newBuffer = reinterpret_cast<T*>(AllocateBuffer((unsigned)(capacity_ * sizeof(T))));
            // Move the data into the new buffer
            ConstructElements(newBuffer, Begin(), End(), MoveTag{});
        }

        // Delete the old buffer
        DestructElements(Buffer(), size_);
        delete[] buffer_;
        buffer_ = reinterpret_cast<unsigned char*>(newBuffer);
    }
}

Reserve函数会重新调整Vector的容量大小。保证newCapacity的大小不小于Vector的size_。当newCapacity与当前的capacity_不相等时,调用VectorBase下的AllocateBuffer接口 来进行新的内存分配,大小为 newCapacity * sizeof(T)。得到新的内存地址之后,调用Move语义的ConstructElements函数来将原来内存中的数据进行搬移。(这里使用Move语言来触发新元素的移动构造函数,避免了内存拷贝带来的额外开销),之后会将原来旧的数据进行析构,并释放对应的内存空间。

/// Move-construct from another vector.
Vector(Vector<T> && vector)
{
    Swap(vector);
}

/// Aggregate initialization constructor.
Vector(const std::initializer_list<T>& list) : Vector()
{
    for (auto it = list.begin(); it != list.end(); it++)
    {
        Push(*it);
    }
}

Vector提供了Move构造函数,内部实现时调用了针对Vector特化的Swap函数,新Vector会继续使用旧Vector的内存空间,不会有多余的内存分配和释放的操作。 Vector另外还提供了初始化列表,这样可以使用{}来直接构造Vector的初始数据,内部调用了Push接口(该构造函数使用了默认构造函数Vector()来初始化Vector成员变量,c++11新语法支持)。

/// Insert elements into the vector using copy or move constructor.
template <class Tag, class RandomIteratorT>
Iterator DoInsertElements(unsigned pos, RandomIteratorT start, RandomIteratorT end, Tag)
{
    if (pos > size_)
        pos = size_;

    const unsigned numElements = end - start;
    if (size_ + numElements > capacity_)
    {
        T* src = Buffer();

        // Reallocate vector
        Vector<T> newVector;
        newVector.Reserve(CalculateCapacity(size_ + numElements, capacity_));
        newVector.size_ = size_ + numElements;
        T* dest = newVector.Buffer();

        // Copy or move new elements
        ConstructElements(dest + pos, start, end, Tag{});

        // Move old elements
        if (pos > 0)
            ConstructElements(dest, src, src + pos, MoveTag{});
        if (pos < size_)
            ConstructElements(dest + pos + numElements, src + pos, src + size_, MoveTag{});

        Swap(newVector);
    }
    else if (numElements > 0)
    {
        T* buffer = Buffer();

        // Copy or move new elements
        ConstructElements(buffer + size_, start, end, Tag{});

        // Rotate buffer
        if (pos < size_)
        {
            std::rotate(buffer + pos, buffer + size_, buffer + size_ + numElements);
        }

        // Update size
        size_ += numElements;
    }

    return Begin() + pos;
}

DoInsertElements接口会在pos处插入由迭代器start,end指定的一堆元素。当插入新元素的个数超出了Vector的容量值,会触发新的内存空间分配。 新的内存分配后,会在pos位置处,构造新的元素,之后会将就的Vector中Pos位置前后的元素搬移到新的Vector中。 如果Vector剩余的容量仍够放下新插入的元素,那么会先在Vector的尾部构造新的元素,之后调用std::rotate函数将原先pos之后的元素与新构造的元素进行置换。

void Push(const T& value)
{
    if (size_ < capacity_)
    {
        // Optimize common case
        ++size_;
        new (&Back()) T(value);
    }
    else
        DoInsertElements(size_, &value, &value + 1, CopyTag{});
}

/// Move-add an element at the end.
void Push(T && value)
{
    if (size_ < capacity_)
    {
        // Optimize common case
        ++size_;
        new (&Back()) T(std::move(value));
    }
    else
        DoInsertElements(size_, &value, &value + 1, MoveTag{});
}

template <class... Args> T& EmplaceBack(Args&&... args)
{
    if (size_ < capacity_)
    {
        // Optimize common case
        ++size_;
        new (&Back()) T(std::forward<Args>(args)...);
    }
    else
    {
        T value(std::forward<Args>(args)...);
        Push(std::move(value));
    }
    return Back();
}

Vector的Push接口提供在尾部进行插入新元素的操作,分别提供了Copy和Move语义的接口。Emplace接口提供了在尾部构造新原色的接口,当Vector已满时,调用Push会触发内存重分配的操作。

/// Erase elements from the vector.
Iterator DoEraseElements(unsigned pos, unsigned count)
{
    assert(count > 0);
    assert(pos + count <= size_);
    T* buffer = Buffer();
    std::move(buffer + pos + count, buffer + size_, buffer + pos);
    Resize(size_ - count);
    return Begin() + pos;
}

DoEraseElements接口从pos处开始,删除count个元素。具体实现方式是通过std::move函数将pos+count以后的元素先搬移到pos处,然后调用Resize析构要删除的元素。 c++11提供的std::move接口会调用Move语义的operator=操作来交换元素。

PODVector

PODVector用来专门存放POD数据类型的元素,这些类型的元素的插入和删除不会触发对应的构造、析构函数。

    void MoveRange(unsigned dest, unsigned src, unsigned count)
    {
        if (count)
            memmove(Buffer() + dest, Buffer() + src, count * sizeof(T));
    }

    /// Copy elements from one buffer to another.
    static void CopyElements(T* dest, const T* src, unsigned count)
    {
        if (count)
            memcpy(dest, src, count * sizeof(T));
    }

对应的MoveRange/CopyElements接口也只是直接调用了mem相关的函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值