【问题标题】:Pop() etiquette流行()礼仪
【发布时间】:2012-05-21 15:09:33
【问题描述】:

情况:我正在实现一个类似列表的容器,该容器支持 Pop() 函数,该函数应该向用户返回存储在容器前面的 const char*。但是,作为实现者,我不确定是否应该返回原始的 const char*(从容器中删除节点指针,但不对 const char* 本身调用 delete),或者是否应该分配新的内存和返回元素的副本。

从课程和项目中,我遇到了那些总是支持复制的人,因此以前返回的引用(来自 getter 等)和指向 const char* 的指针不会改变弹出的版本,但由于这种方法需要额外的分配和strcpy,我想我会问是否只是简单地返回原始的、未删除的指向 const char 的指针而不删除它真的是一个问题。这是 WITH 分配和复制方法的代码 sn-p(然后删除原始引用):

const char* LinkedList::PopHeadString()
{
    node* deletehead = head_;
    char* output = NULL;

    if (head_ != NULL && GetHeadType() == STRING) {
        output = new char[strlen(head_->en.data.str) + 1];
        strcpy(output, head_->en.data.str);
        head_ = head_->next;
        delete deletehead->en.data.str;
        delete deletehead;
        --nEntries_;
    }
    return output;
}

既然 Pop() 是一种常见的容器操作,我想我会问一般的方法是什么。

【问题讨论】:

  • STL 的解决方案是pop() 根本不返回任何东西。如果用户想要该对象,他们会使用back() 获得对它的引用——当然,如果他们在调用pop() 后需要使用该对象,则需要复制。此外,指针的 STL 容器仅假定指针的所有权,而不是指向的对象的所有权。
  • .. 因此,如果我遵循 STL 约定,假设存储(插入)指针的所有权但自己从未调用 new/delete,那么这是否也意味着容器的 STL 复制构造函数是浅拷贝?例如,如果我的一个成员函数应该返回一个新列表,其中包含第一个元素之后的所有元素(即 GetRemainder),那么我将创建一个副本列表,其中的指针指向与原始相同的动态内存地址?
  • 是的。容器对值语义进行操作,因此如果您创建std::list<int*> 类型的对象,它会将指针视为值。因此,如果您复制列表,它会复制指针,并且不会尝试猜测您使用指针的目的。请注意,这通常是正确的。在我的观察中,C 风格的字符串确实是唯一常见的例外。当然,普遍的看法是你不应该在 C++ 中使用 C 风格的字符串:你应该使用 std::string
  • 感谢您的建议。我很想使用字符串,但除了我被禁止使用 STL 之外,我还考虑让我的容器异构(每个元素可能是一个字符串、另一个分支列表或要执行的函数指针在后面的元素上),可能有一个联合,在这种情况下,包含指针可能比包含对象更有效。不过,浅拷贝仍然是最好的吗? typedef union { const char* str; LinkedList* list; int (*proclist)(int, LinkedList); // func to execute on list } content;

标签: c++ list oop copying


【解决方案1】:

如果你不复制就返回指针,你必须决定谁拥有指针。拥有它的人有责任在不再需要它时将其删除。这可能是一个非常困难的问题,尤其是当您的代码变得更复杂时。复制语义更容易推理。

在您的具体示例中,首先要更改的是使用std::string 而不是const char * 来表示字符串。如果您按值返回 std::string,它将为您处理复制。

如果你真的想防止复制,但仍然优雅地管理生命周期,你应该考虑使用std::shared_ptr<std::string>(如果你没有 C++11 编译器,则使用boost::shared_ptr<std::string>)。 shared_ptr 使用引用计数来确定它指向的对象何时应该被释放,从而减轻您手动管理内存的负担,这是一个好主意。

【讨论】:

  • 这是一个很好的观点——所有权。我不知道谁拥有指针,我不知道如何回答这个问题。例如,现在,当该类的用户预先添加一个新的 const char* 元素时,我目前已将容器编程为不仅包含指针,而且复制指向的元素。我没有出于任何特殊原因这样做,除了一些临时担心,如果我没有为包含的指针创建一个新副本,那么插入的元素可能会在容器之前超出范围(即容器甚至被传递调用函数返回后)
  • 如果容器在添加元素时创建了一个副本,那么您可以认为它是该对象的所有者。
  • ...但现在我进一步考虑,在堆上声明的内存不应该超出范围,所以也许真的没有理由自动创建一个新的副本插入。如果将指针传递给 InsertElem(elem* ptr),那么我想我们可以采用简单存储 ptr 的 STL 方法吗?我担心有人可能会调用 Insert(&local_obj),然后当调用者退出时,local_obj 消失了,容器持有未定义数据的 ptr。我的想法在哪里错误,容器何时应该积极复制插入的元素?
  • @Cindeselia 如果您按照示例返回指针,则调用者拥有它,您应该记录它。您可以通过返回 unique_ptr 来明确这些语义。但我认为在这里按价值返回是迄今为止最好的选择。
  • 容器创建了一个副本,因为我是这样编程的。但我承认我从来没有想过所有权,因为我从来没有被教导过。令人尴尬的是,在我学习的课文和讲座中,从来没有解决过这个问题。简而言之(或资源,如果你可以推荐的话),如果我正在实现一个通用容器类而对它周围的任何其他知识一无所知,我该如何做出决定?
猜你喜欢
  • 2012-12-06
  • 2012-03-07
  • 1970-01-01
  • 2011-01-15
  • 1970-01-01
  • 1970-01-01
  • 2012-11-02
  • 2011-01-25
  • 2012-05-02
相关资源
最近更新 更多