我需要重置一个shared_ptr之前从一个矢量删除它
Do I need to reset a shared_ptr before removing it from a vector?
我用std::shared_ptr写了一个非常简单的c++程序。
代码如下:
/*
** Resource class definition
*/
class Resource
{
public:
std::string m_Name;
Resource(void){}
Resource(std::string name)
: m_Name(name)
{
}
std::string const &GetName(void) const
{
return (this->m_Name);
}
};
namespace Predicate
{
/*
** Predicate - Delete a specific node according to its name
*/
template <typename T>
struct DeleteByName
{
DeleteByName(std::string const &name);
bool operator()(T &pData);
std::string m_Name;
};
//Initialization
template <typename T>
DeleteByName<T>::DeleteByName(std::string const &name)
: m_Name(name)
{
}
//Surcharges
template <typename T>
bool DeleteByName<T>::operator()(T &pData)
{
if (pData->GetName() == this->m_Name)
{
pData.reset();
return (true);
}
return (false);
}
}
/*
** Remove a specific node according to its name - WORKS
*/
static void RemoveByName__CLASSIC__OK(std::string const &name, std::vector<std::shared_ptr<Resource>> &resourceList)
{
std::vector<std::shared_ptr<Resource>>::iterator It = resourceList.begin();
std::vector<std::shared_ptr<Resource>>::iterator It_dest;
for (; It != resourceList.end(); ++It) {
if (!(*It)->GetName().compare(name))
{
It_dest = It;
}
}
It_dest->reset();
resourceList.erase(It_dest);
}
/*
** Remove a specific node according to its name - NOT WORK
*/
static void RemoveByName__CLASSIC__NOT_OK(std::string const &name, std::vector<std::shared_ptr<Resource>> &resourceList)
{
std::vector<std::shared_ptr<Resource>>::iterator It = resourceList.begin();
for (; It != resourceList.end(); ++It) {
if (!(*It)->GetName().compare(name))
{
It->reset();
resourceList.erase(It);
}
}
}
static std::vector<std::shared_ptr<Resource>>::const_iterator FindByName__PREDICATE__OK(
std::string const &name, std::vector<std::shared_ptr<Resource>> &resourceList)
{
return (std::find_if(resourceList.begin(),
resourceList.end(), Predicate::FindByName<std::shared_ptr<Resource>>(name)));
}
/*
** Remove a specific node according to its name using std::remove_if algorithm with the predicate 'DeleteByName' - WORKS
*/
static void RemoveByName__PREDICATE__OK(std::string const &name, std::vector<std::shared_ptr<Resource>> &resourceList)
{
if (FindByName__PREDICATE__OK(name, resourceList) != resourceList.end())
resourceList.erase(std::remove_if(
resourceList.begin(), resourceList.end(), Predicate::DeleteByName<std::shared_ptr<Resource>>(name)));
}
/*
** Entry point
*/
int main(void)
{
std::vector<std::shared_ptr<Resource>> resourceList;
std::shared_ptr<Resource> rsc_A(new Resource("resource_a"));
std::shared_ptr<Resource> rsc_B(new Resource("resource_b"));
std::shared_ptr<Resource> rsc_C(new Resource("resource_c"));
resourceList.push_back(rsc_A);
resourceList.push_back(rsc_B);
resourceList.push_back(rsc_C);
PrintResourceList(resourceList);
RemoveByName__PREDICATE__OK("resource_as", resourceList);
PrintResourceList(resourceList);
getchar();
return (0);
}
我只是想知道我是否从包含共享指针的std::vector中擦除节点,如果我必须在调用'erase'方法之前调用'reset'方法来销毁共享指针。我认为,如果我只是销毁节点,而不调用函数"重置",共享指针应该仍然被销毁。对吗?
另外,我不明白为什么函数'RemoveByName__CLASSIC__NOT_OK'失败了。我不明白为什么我必须声明一个'It_dest'来在循环期间存储迭代器(参见方法'RemoveByName__CLASSIC__OK'),并最终在函数结束时擦除节点。这个问题只是在使用共享指针时发生的。有人知道吗?
您不必手动重置shared_ptr,这是在析构函数中完成的。当您擦除它时,对象将被销毁,从而减少引用计数。
您的RemoveByName__CLASSIC__NOT_OK
函数失败,因为您在擦除指向元素后使用迭代器。在std::vector::erase
之后,迭代器将失效,不能再使用。erase
返回下一个迭代器
static void RemoveByName__CLASSIC__NOT_OK(std::string const &name, std::vector<std::shared_ptr<Resource>> &resourceList)
{
for (auto It = resourceList.begin();
It != resourceList.end(); ) {
if (!(*It)->GetName().compare(name))
{
It = resourceList.erase(It);
}
else
{
++It;
}
}
}
我认为使用remove_if的实现更具可读性。
RemoveByName__CLASSIC__NOT_OK
执行未定义行为
当从std::vector
中擦除时,所有迭代器和对处或后元素的引用都无效。这意味着它们不能被解引用、比较或其他任何操作,只能在不调用未定义行为的情况下安全地覆盖。
现在,UB经常"神奇地完成你认为它应该做的事情",所以失败的崩溃对你没有帮助。
碰巧的是,如果RemoveByName__CLASSIC__NOT_OK
在erase
之后立即执行break
,那么它将被很好地定义。
RemoveByName__CLASSIC__OK
将擦除延迟到迭代完成之后。它有许多问题,包括执行未定义的行为,如果具有该名称的元素不存在,不处理重复的名称,等等。如果最后一个元素存在,则擦除与名称匹配的元素,否则执行未定义行为。您可能希望每个元素都,并且/或首先擦除以节省时间。(如果你真的想要最后一个,向后迭代并擦除你找到的第一个)。
shared_ptr
之前, .reset()
将对象的可能销毁移动到.reset()
,而不是在std::shared_ptr
的内部,这有时可能是有用的(因为当您在内部时对std::vector
的任何和所有访问都是UB)。我经常做的一件事是swap
或move
将shared_ptr
从容器中取出,.erase
将其从容器中取出,然后.reset
或只是让本地shared_ptr
副本超出范围。
你的RemoveByName__PREDICATE__OK
也坏了,可以表现出未定义的行为,基本上做错误的事情,如果除了1个元素匹配谓词被发现。将erase
子句末尾的);
更改为, resourceList.end());
,这样就不会擦除一个元素,而是擦除从remove_if
返回值到vector
末尾的所有元素。
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 从链接列表c++中删除一个项目
- 删除映射和分割错误中的一个过去结束元素
- 使用另一个字符串从字符串中删除空格
- 编写一个函数来删除单链表中的节点(尾部除外),仅授予对该节点的访问权限
- 创建了一个链表,但如何删除 c++ 中的"所有"节点
- 如何知道地图中的最后一个元素是否被删除?
- G++ 发出警告,要求删除一个代码的数组,但不删除另一个代码的数组
- 动态分配列表 - 创建一个函数,用于删除所有包含偶数值的元素
- 如何删除矢量中的重复值,最后一个除外
- C++ 删除存在于另一个矢量中的矢量项,同时保留顺序
- 当类型适当的构造函数可用时,为什么一个编译器尝试使用已删除的副本构造函数
- 在 c++ 中打开文件、删除标点符号并追加到另一个文件中
- 删除链接列表中剩余的最后一个节点
- 编译器在C++中调用另一个函数时,在参数中查找已删除的构造函数
- 避免在使用链接列表从 deque 中删除最后一个节点时出现内存泄漏
- 在C++中为二叉搜索树创建一个删除函数
- 当编译一个删除停止字并应用大小写折叠的程序时,会出现一个奇怪的代码
- 为什么C++仍然有一个删除[]和一个删除运算符
- 一个删除行的函数