作为练习,我试图在不使用模板的情况下编写类似std::vector
的类。它拥有的唯一类型是std::string
。
以下是strvec.h
文件:
class StrVec
{
public:
//! Big 3
StrVec():
element(nullptr), first_free(nullptr), cap(nullptr)
{}
StrVec(const StrVec& s);
StrVec&
operator =(const StrVec& rhs);
~StrVec();
//! public members
void push_back(const std::string &s);
std::size_t size() const { return first_free - element; }
std::size_t capacity() const { return cap - element; }
std::string* begin() const { return element; }
std::string* end() const { return first_free; }
void reserve(std::size_t n);
void resize(std::size_t n);
//^^^^^^^^^^^^^^^^^^^^^^^^^^^
private:
//! data members
std::string* element; // pointer to the first element
std::string* first_free; // pointer to the first free element
std::string* cap; // pointer to one past the end
std::allocator<std::string> alloc;
//! utilities
void reallocate();
void chk_n_alloc() { if (size() == capacity()) reallocate(); }
void free();
void wy_alloc_n_move(std::size_t n);
std::pair<std::string*, std::string*>
alloc_n_copy (std::string* b, std::string* e);
};
三个string*
,element
,first_free
和cap
可以被视为:
[0][1][2][3][unconstructed elements]
^ ^ ^
element first_free cap
在实施成员resize(size_t n)
时,我遇到了问题。说,v.resize(3)
被调用。因此,指针first_free
必须向前移动一个位置并指向[3]
。类似的东西:
[0][1][2][3][unconstructed elements]
^ ^ ^
element first_free cap
我的问题是我该如何处理[3]
?把它留在那里不受影响?或者摧毁它:
if(n < size())
{
for(auto p = element + n; p != first_free; /* empty */)
alloc.destroy(p++);
first_free = element + n;
}
此处是否需要代码alloc.destroy( somePointer)
?
答案 0 :(得分:3)
是的,当你使用小于当前矢量大小的参数调用destroy
时,你应该对从向量中删除的元素调用resize()
。这也是std::vector
的作用。
请注意destroy
仅调用这些元素上的析构函数;它不会释放任何空间(这是错误的)。
由于您正在处理std::string
,如果您确定稍后使用新值重新初始化相同的std::string
对象,您可能认为可以不做破坏。但首先,你不能确定以后会在同一个地方存储一个新字符串,其次,对于新字符串,将创建一个新对象(使用placement-new,而不是copy-assignment),泄漏内存前一个字符串(其析构函数永远不会被调用)。
答案 1 :(得分:1)
你应该做什么取决于你如何初始化element
,因为你需要你的代码保持一致。
如果你使用new std::string[n]
来创建字符串数组,那么它们都将被预先初始化,当你必须使用delete[]
来释放它们时,它们的析构函数都会被运行。因此,除非您确定将再次放置new
有效对象,否则不得在间隔期间手动调用析构函数。
如果使用static_cast<std::string*>(new char[sizeof(std::string) * n])
之类的东西来创建未初始化内存的缓冲区,那么你必须在适当的时候完全负责调用每个元素的构造函数和析构函数
使用第一个选项,您不需要为resize(3)
执行任何操作,但可以在字符串上调用.clear()
,以便在需要时释放一些内存。
使用第二个选项,你必须触发[3]
的析构函数(除非你保留一些其他元素最终需要销毁的记录,这似乎是一个笨拙的模型)。
问题与在程序期间的不同时间为“正在使用”的单个字符串具有内存相同。您是在第一次使用之前花时间构建它然后分配给它,还是将其保留为未初始化然后使用展示位置new
进行复制构建?您是否{@ 1}}未使用或毁坏它?这两种模型都可以仔细实施。第一种方法往往更容易正确实现,当数组容量远远大于最终使用的元素数时,第二种方法效率稍高。