对于我自己的教育,我正在尝试学习如何在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
的行为,也就是说,推回类的实例应该创建这样的类的全新临时实例。
有人可以帮助我理解我做错了什么吗?
答案 0 :(得分:1)
问题很可能是push_back
函数中的这一行:
m_data = new T[m_capacity]();
这将导致创建m_capacity
个T
个对象,因此会m_capacity
调用T
构造函数。如果T
构造函数很昂贵(更不用说一些初学者在构造函数中做输入和其他事情),那就太糟糕了。
std::vector
最有可能做的是保留字节的缓冲区,然后在推回时, placement new 在某些地方构建一个对象在缓冲区中的位置。