有必要在重新构造字符串之前先销毁它吗
Is it nessary to destroy a string before constructing it again?
作为一个练习,我尝试在不使用模板的情况下编写一个类似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)
代码吗?
是的,当然,当使用小于矢量当前大小的参数调用resize()
时,您应该对从矢量中删除的元素调用destroy
。std::vector
也是这样做的。
注意,destroy
只调用那些元素上的析构函数;它不会释放任何空间(这是错误的)。
由于您正在处理std::string
,您可能认为如果您确信稍后使用新值重新初始化相同的std::string
对象,则可以不进行销毁。但首先,您不能确定新字符串稍后是否会存储在同一个位置,其次,对于新字符串,将创建一个新对象(使用placement-new,而不是复制赋值),从而泄漏前一个字符串(其析构函数永远不会被调用)的内存。
您应该做什么取决于您初始化element
的方式,因为您需要代码保持一致。
-
如果您使用
new std::string[n]
创建字符串数组,那么它们都将被预初始化,并且当您稍后必须使用delete[]
来释放它们时,它们的析构函数都将运行。出于这个原因,除非您确信将再次在其中放置-new
一个有效对象,否则您不能在中间时间手动调用析构函数。 -
如果您使用类似
static_cast<std::string*>(new char[sizeof(std::string) * n])
的东西来创建未初始化内存的缓冲区,那么您必须全权负责在适当的时候调用每个元素的构造函数和析构函数
使用第一个选项,您不需要为resize(3)
做任何事情,但如果需要,可以在字符串上调用.clear()
以释放一些内存。
对于第二个选项,您必须触发[3]
的析构函数(除非您保留了最终需要销毁哪个元素的其他记录,这似乎是一个笨拙的模型)。
这些问题与在程序的不同时间"使用"单个字符串的内存相同。你是在第一次使用之前花时间构建它,然后分配给它,还是让它未初始化,然后用放置new
复制构建它?你是在未使用时clear
还是销毁它?任何一种模型都可以通过仔细的实现来工作。第一种方法往往更容易正确实现,当阵列容量远大于最终使用的元素数量时,第二种模型的效率略高。
- 如何从字符串向量构造对象并避免复制?
- 正在通过const-ref未定义的行为捕获新构造的对象
- 具有字符串文本构造函数的类不适用于 const 引用初始化
- Unicode字符串Visual C 构造函数
- 如何销毁通过"放置新"构造的无析构函数类型
- 动态字符串阵列构造函数和空返回
- 新构造的对象作为默认模板函数参数
- 设计字符串类构造函数 C++
- 派生类的新构造函数未生成:"overloaded member function not found"
- 如何从字符串位置构造CSTRING/STD :: String
- 字符串移动构造函数是如何工作的
- 将类构造函数分配给具有参数的新构造函数C++
- 移动 ICU 字符串的构造函数
- C++字符串成员构造
- 模板化函数和放置新构造函数参数
- 如何在 getline 中使用字符串流构造函数
- 构造函数与新构造函数
- c++如何阅读这个创建新构造函数的代码
- 空字符串在构造函数中被解释为bool
- 初始化现有变量/对象的新构造函数