自定义容器在保留空间时不必要地创建新元素实例

时间:2017-11-18 06:05:42

标签: c++ constructor containers allocation

对于我自己的教育,我正在尝试学习如何在C ++中实现高效的自定义容器。我现在有一个自定义矢量类型的基本工作版本。但是,出于某种原因,当向量必须扩展以适应更多元素时(在这种情况下调用其内部'保留'函数),它会创建额外的元素副本。

为了帮助解释我的意思,我在下面展示了一个可重复的最小例子。让CustomVector类的最低版本如下所示:

template<class T>
class CustomVector
{
private:
    size_t m_size = 0;
    size_t m_capacity = 1;
    T *m_data = nullptr;

public:
CustomVector()
{
}

CustomVector(const size_t new_capacity)
{
    m_capacity = new_capacity;
    m_size = 0;
    m_data = new T[m_capacity]();
}

~CustomVector()
{
    if (m_data != nullptr)
        delete[] m_data;
}

void reserve(size_t new_capacity)
{
    if (m_data == nullptr)
    {
        m_capacity = new_capacity;
        m_size = 0;
        m_data = new T[m_capacity]();
    }
    else if (new_capacity > m_capacity)
    {
        T* new_data = new T[new_capacity]();
        memmove(new_data, m_data, (m_size) * sizeof(T));
        delete[] m_data;
        m_capacity = new_capacity;
        m_data = new_data;
    }
}

void push_back(const T & value)
{
    if (m_data == nullptr)
    {
        m_capacity = 1;
        m_size = 0;
        m_data = new T[m_capacity]();
        m_data[0] = value;
    }
    else if (m_size + 1 >= m_capacity)
    {
        reserve(m_capacity*2);
    }
    else
    {
        m_data[m_size-1] = value;
        m_size++;
    }
}

};

现在,为了便于查看问题,我还创建了一个名为Object的类。创建的此类的每个新实例都会自动获得唯一的ID号:

class Object
{
private:
    static int idCounter;
public:
    int id;
    Object()
    {
        id = idCounter;
        idCounter++;
    }
};
int Object::idCounter = 0;

最后,以下是此示例的main函数的样子:

int main()
{
    CustomVector<Object> objects; //comment this line...
    //std::vector<Object> objects; //...and uncomment this to try with std::vector
Object x;
printf("%d\n", x.id);
objects.push_back(x);

Object y;
printf("%d\n", y.id);
objects.push_back(y);

Object z;
printf("%d ", z.id);

system("Pause");
return 0;

}

使用我的CustomVector作为容器的输出是:

  

0 2 5

使用std::vector作为容器的输出是:

  

0 1 2

对我来说,理想的行为恰恰是std::vector的行为,也就是说,推回类的实例应该创建这样的类的全新临时实例。

有人可以帮助我理解我做错了什么吗?

1 个答案:

答案 0 :(得分:1)

问题很可能是push_back函数中的这一行:

m_data = new T[m_capacity]();

这将导致创建m_capacityT个对象,因此会m_capacity调用T构造函数。如果T构造函数很昂贵(更不用说一些初学者在构造函数中做输入和其他事情),那就太糟糕了。

std::vector最有可能做的是保留字节的缓冲区,然后在推回时, placement new 在某些地方构建一个对象在缓冲区中的位置。