C++中的指针容器

Posted

技术标签:

【中文标题】C++中的指针容器【英文标题】:container of pointers in c++ 【发布时间】:2019-06-17 01:58:27 【问题描述】:

我正在阅读“a Introduction to design patterns in c++ with qt”一书。在第 6 章中(参见本书链接https://www.ics.com/designpatterns/book/containersofpointers.html),作者正在尝试编写一个继承自 QList 的库类。在示例 6.35 中定义了 addRefItem 函数。作者有非常奇怪的(至少对我来说)处理指针的方式。

void Library::addRefItem(RefItem*& refitem)  

这里,作者使用了指针引用*&,他解释说“这样删除后的空赋值是可能的”。我认为这与最后两行有关。

   QString isbn(refitem->getISBN());
   RefItem* oldItem(findRefItem(isbn));
   if(oldItem==0)
      append(refitem);
   else 
      qDebug() << isbn << " Already in list:\n"
               << oldItem->toString()
               << "\nIncreasing number of copies " 
               << "and deleting new pointer." ;
      int newNum(oldItem->getNumberOfCopies() + refitem->getNumberOfCopies());
      oldItem->setNumberOfCopies(newNum);
      delete refitem;                         
      refitem = 0;                            
   

我不明白最后两行是做什么的。为什么需要删除refitem。函数返回后它无论如何都会被破坏,对吧?然后为什么refitem需要赋值为0。

removeRefItem函数中,也有类似的一行,delete ref,见下文。谁能帮我理解所有这些?非常感谢。

int Library::removeRefItem(QString isbn) 
   RefItem* ref(findRefItem(isbn));
   int numCopies(-1);
   if(ref) 
      numCopies = ref->getNumberOfCopies() - 1;
      if(numCopies== 0) 
         removeAll(ref);
         delete ref;
      
      else
         ref->setNumberOfCopies(numCopies);
   
   return numCopies;

【问题讨论】:

任何堆栈分配的对象将在其范围退出时被破坏。您的 误解是认为在指针的情况下意味着指针将被删除。事实并非如此。怎么会这样?您不能假设任何给定的指针都指向堆分配的对象,或者即使它确实如此,也希望将其删除。如果您仍想使用指针的副本怎么办?当指针被销​​毁时,什么都不会发生。 非常感谢,你是对的,我认为删除意味着指针将被破坏。事实证明它只是释放内存。在退出其作用域之前,指针仍然存在。 这就是关于指针的重要事情,指针发生的事情和被指向的事物发生的事情是不同的。 delete ptr 确实会破坏指向的内容(并释放内存),它对指针本身没有任何作用。同样,当指针被销​​毁时,它对所指向的内容没有任何作用。 【参考方案1】:

你觉得这很奇怪。你应该。如果可以避免的话,不要做这样的事情。总的来说,Avoid manual memory management 喜欢瘟疫。看看你能不能replace this with std::shared_ptrs。这将需要一些工作,但结果会更加强大。

函数返回后它无论如何都会被破坏,对吧?

没有。 RefItem*&amp; refitem 提供了对指针的引用,但由于提供了引用,因此您知道传递给函数的任何对象都不在 addRefItem 范围内,因为它来自其他地方。如果要自动销毁,会在这个其他地方销毁。

我不明白最后两行是做什么的。为什么需要删除“refitem”。函数返回后它无论如何都会被破坏,对吧?然后为什么需要将“refitem”分配为0。

你不知道refitem指向的对象是如何分配的,是否是automatically or dynamically allocated所以你不知道它什么时候会超出范围,但它不会在@987654330中被自动销毁@。使用refitem,特别是delete refitem;,表明它是动态分配的。如果不是程序注定要Undefined Behaviour。

为什么ref的对象被销毁了?我们已经有一个了。为什么有两个?此代码将相同的RefItems 聚合为单个RefItem,维护该对象被复制的次数的计数,该计数存储在列表中。现在冗余的对象被销毁。这使得RefItem 成为reference-counted object。

代码块 1 显示,如果项目已经在列表中,则提供的对象将被销毁并使用 delete refitem; 释放,指向它的指针使用 refitem = 0; 清空,以便更容易检测到该对象不存在更多的。如果此函数的调用者尝试使用空指针,则会发生未定义行为,但绝大多数系统,我在过去 20 年左右所做的一切工作,都会将使用检测为无效并崩溃程序。

这个我不太明白。与其将指针设为空,不如将指针更新为指向列表中吸收并替换传入的指针的项。更完整的示例可以更好地解释这种选择。

顺便说一句,不要使用0 将指针设为空。与nullptr(C++11 或更高版本)或NULL(C++11 之前)相比,使用0 更难辨别代码的用途。 0 有很多含义。 nullptr 有一个。

在removeRefItem函数中,也有类似的一行,“delete ref”,见下文。谁能帮我理解所有这些?非常感谢。

在第二个代码示例中,removeRefItem 销毁并释放 ref,如果未完成副本的数量,即引用计数,减少到 0。

附录:既然可以使用std::unique_ptr 轻松实现此代码,为什么还要在此答案的序言中推荐std::shared_ptr?因为这段代码似乎正在实现一个引用计数指针。我可能完全错了,在这种情况下std::unique_ptr 是要走的路。只需从容器中删除 unique_ptr 并让它退出范围,让系统为您处理破坏。如果这是一个用于从列表中检出和检入指向同一对象的指针的系统,std::shared_ptr 会为您完成所有这些工作,并且做得很好且安全。为什么要对std::unique_ptr 进行kit-bash 来完成这项工作?

【讨论】:

shared_ptr?真的吗?除非有充分的理由分享,否则我会使用 unique_ptr。 通常我会同意你的看法,@Thomas,并认为你可以用unique_ptr 重写这个并且同样开心。与引用计数器的整个交易表明人们正在检查并持有指针,而这种行为正是shared_ptr 旨在简化的。列表可以消失,列表周围的所有脚手架都可以消失......这个例子有点奇怪。当一本书的所有副本都归还图书馆时,这本书不会不复存在。【参考方案2】:

作者使用了指针引用*&,他解释说“这样删除后的空赋值是可能的”。我认为这与最后两行有关。

这意味着如果你只是把它作为一个指针传递,你仍然可以做refitem = 0;,但是当函数返回时这个值不会被传递。所以作为指针引用传递来实现这一点。

我不明白最后两行是做什么的。为什么需要删除“refinem”。

refItem 是一个指针(通过设计指向分配的内存),必须在某处删除它。查看代码,作者正在分配此功能删除它的责任。

关键是作者要确保addRefItem()函数返回时,如果删除成功,refitem的值应该设置为null。如果指针没有通过引用传递,这是不可能的。

【讨论】:

谢谢,现在我明白了。作者希望将 RefItem 从函数调用外部复制的指针设为 null。不使用引用,只会使 Refitem 为空。 @ptr2love 作者想确定addRefItem()函数何时返回,如果已删除refitem,其值为null。如果它没有通过引用传递,它的值将与传递给函数的值相同。 如果它回答了您正在寻找的内容,请投票并接受其中一个答案作为已接受的答案。

以上是关于C++中的指针容器的主要内容,如果未能解决你的问题,请参考以下文章

STL 容器中的 const 指针

盛最多水的容器 双指针解法 C++

C++容器

C++中的迭代器

重学C++:笔记C++基础容器&C++指针引用

重学C++:笔记C++基础容器&C++指针引用