我使用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'来销毁共享指针。我想如果我只是在没有调用函数'reset'的情况下销毁节点,那么共享指针仍然应该被销毁。是吗?
另外,我不明白为什么函数'RemoveByName__CLASSIC__NOT_OK'失败。我不明白为什么我必须在循环期间声明一个'It_dest'来存储迭代器(参见方法'RemoveByName__CLASSIC__OK'),最后擦除函数末尾的节点。这个问题恰好发生在共享指针上。有没有人有想法?
答案 0 :(得分:4)
您不必手动重置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的实现更具可读性。
答案 1 :(得分:1)
RemoveByName__CLASSIC__NOT_OK
执行未定义的行为。
当您从std::vector
中删除时,所有迭代器和对 元素 之后的元素的引用都将失效。这意味着它们不能被解除引用或比较,或者在不调用未定义行为的情况下安全地覆盖它们。
现在,UB经常“做你觉得它应该神奇地做的事情”,所以失败并没有帮助你。
如果发生这种情况,如果RemoveByName__CLASSIC__NOT_OK
在break
之后立即执行erase
,那么它就会很明确。
RemoveByName__CLASSIC__OK
推迟擦除,直到完成迭代。它有许多问题,包括如果不存在具有该名称的元素则执行未定义的行为,不处理重复的名称等。它会删除与名称匹配的 last 元素(如果存在),否则会未定义的行为。您可能需要每个元素,和/或删除您发现的第一个以节省时间。 (如果你真的想要最后,请向后迭代并删除你找到的第一个。)
.reset()
之前 shared_ptr
会将对象的可能破坏移至.reset()
,而不是在std::shared_ptr
的内部,有时 非常有用(因为当您处于胆量状态时,对std::vector
的任何和所有访问都是UB)。我要做的一件事是swap
或move
shared_ptr
出容器,.erase
来自容器,然后是.reset
或者只是让本地{ {1}}副本超出范围。
您的shared_ptr
也会被破坏并且可能会出现未定义的行为,如果找到除了与谓词匹配的1个元素之外的任何内容,则基本上会出错。将RemoveByName__PREDICATE__OK
子句末尾的);
更改为erase
,以便不会删除一个元素,而是删除从, resourceList.end());
的返回值到结尾的所有内容。 remove_if
。