【问题标题】:Is it nessary to destroy a string before constructing it again?在再次构造之前销毁字符串是否必要?
【发布时间】:2014-01-22 16:22:09
【问题描述】:

作为练习,我尝试在不使用模板的情况下编写类似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*elementfirst_freecap 这三个可以认为是:

[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)吗?

【问题讨论】:

    标签: c++ string memory-management


    【解决方案1】:

    你应该做什么取决于你如何初始化element,因为你需要你的代码保持一致。

      1234563出于这个原因,你不能在中间手动调用析构函数,除非你确定你会在那里再次放置-new一个有效的对象。
    • 如果您使用static_cast&lt;std::string*&gt;(new char[sizeof(std::string) * n]) 之类的东西来创建未初始化内存的缓冲区,那么您必须全权负责在适当的时间调用每个元素的构造函数和析构函数

    使用第一个选项,您无需为resize(3) 执行任何操作,但可以根据需要在字符串上调用.clear() 以释放一些内存。

    使用第二个选项,您必须触发 [3] 的析构函数(除非您保留一些其他记录来记录最终需要销毁哪个元素,这似乎是一个笨拙的模型)。

    这些问题与仅在程序期间的不同时间为“正在使用”的单个字符串提供内存相同。您是在第一次使用之前花时间构建它然后分配给它,还是让它未初始化然后使用放置new 复制构建它?你是clear它在未使用时还是销毁它?任何一种模型都可以在仔细实施的情况下工作。第一种方法更容易正确实现,当数组容量远大于最终使用的元素数量时,第二种方法效率略高。

    【讨论】:

    • 我假设他使用alloc.allocate 为字符串分配内存。这对应于您的第二个选项。
    【解决方案2】:

    是的,当然,当使用小于向量当前大小的参数调用resize() 时,您应该对从向量中删除的元素调用destroystd::vector 也是这样做的。

    注意destroy 只在那些元素上调用析构函数;它不会释放任何空间(这是错误的)。

    由于您正在处理std::string,如果您确定稍后使用新值重新初始化相同的std::string 对象,您可能认为您可以不破坏。但首先,您不能确定以后是否将新字符串存储在同一位置,其次,对于新字符串,将创建一个新对象(使用placement-new,而不是copy-assignment),泄漏内存前一个字符串(其析构函数永远不会被调用)。

    【讨论】:

    • 你能告诉我更多吗?我试图在不破坏它的情况下对其进行编码。似乎它仍然可以正常工作。你能告诉我这样做有什么好处吗?
    • 答案的关键是“泄露内存”。如果不销毁它,每次执行此过程时都会分配一些不可用的内存,这意味着使用此代码的应用程序将缓慢但不断地增加它们在任何一次运行期间使用的内存量。如果您运行一些简短的测试,这不会导致任何明显的问题,但是如果您可以在重复调用此代码的地方进行更长的测试,您的应用程序可能会耗尽内存并关闭,或者可能会不必要地减慢系统的其余部分.
    • @Alan.W 抱歉,我写完答案后不得不离开。与此同时,其他人提供了进一步的澄清。还有问题吗?
    • @jogojapan 不用担心。从您和其他人的回复中学到了很多。现在没有更多问题了。
    猜你喜欢
    • 1970-01-01
    • 2018-06-18
    • 1970-01-01
    • 2012-02-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-05
    • 2021-06-05
    相关资源
    最近更新 更多