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_ptr
s。这将需要一些工作,但结果会更加强大。
函数返回后它无论如何都会被破坏,对吧?
没有。 RefItem*& refitem
提供了对指针的引用,但由于提供了引用,因此您知道传递给函数的任何对象都不在 addRefItem
范围内,因为它来自其他地方。如果要自动销毁,会在这个其他地方销毁。
我不明白最后两行是做什么的。为什么需要删除“refitem”。函数返回后它无论如何都会被破坏,对吧?然后为什么需要将“refitem”分配为0。
你不知道refitem
指向的对象是如何分配的,是否是automatically or dynamically allocated所以你不知道它什么时候会超出范围,但它不会在@987654330中被自动销毁@。使用refitem
,特别是delete refitem;
,表明它是动态分配的。如果不是程序注定要Undefined Behaviour。
为什么ref
的对象被销毁了?我们已经有一个了。为什么有两个?此代码将相同的RefItem
s 聚合为单个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++中的指针容器的主要内容,如果未能解决你的问题,请参考以下文章