删除 std::shared_ptr 时标量删除错误

问题描述

我想知道如何删除 Block 类的子级。我试图用原始指针来做。我不知道为什么,但它没有用。我收到标量删除错误。我现在尝试使用 std::shared_ptr 来做到这一点。我也没有工作。我正在移除孩子:

void Block::remove(Block* block)
    {
        std::shared_ptr<Block> ptr(block);
        auto it = std::find(children.begin(),children.end(),ptr);

        if (it != children.end())
        {
            *it = NULL;
            children.erase(it);
        }
    }

删除器是:

Block::~Block()
    {
        for (auto& child : this->children)
        {
            child = NULL;
        }


        if (!this->children.empty())
            this->children.clear();
    }

根据调试过程,找到ptr变量,然后删除。在删除所有内容时,直到最后一行都运行良好,在那里我收到了标量删除错误。仅作说明:children 变量的类型为 std::vector<std::shared_ptr<Block>>

编辑: 完整代码在这里

enter image description here

。所有块函数都定义在 AOLGuiLibrary/source/Block.cpp

解决方法

您将 shared_ptr 与原始指针混合在一起,这是一种不好的做法。如果我理解正确,您将块存储在共享指针的向量中,这意味着该向量对这些对象拥有所有权。当您从原始指针创建一个额外的 shared_ptr 时,您将创建另一个拥有同一对象所有权的对象:

std::shared_ptr<Block> ptr(block);

结果,您会尝试删除对象两次,这会导致未定义的行为。

首先:你需要共享指针吗?考虑使用 unique_ptr 作为一种不易出错的想法(如果只有一个指针对象拥有所有权,则更容易理解谁以及何时会销毁底层对象)。

接下来,永远不要在这种情况下使用原始指针:只有在不存储/删除对象时才可以使用原始指针。并且肯定不要从已经存储在其中之一中的东西中创建智能指针。

,

如果block指向数组代码,这个代码是非法的,因为对于new[]你必须调用delete[],而std::shared_ptr<Block>会调用delete .之后,堆将被破坏,进一步的操作可能会失败。如果 blocks 指向数组元素,也会发生同样的情况。如果 blocks 指向不在堆中的对象,也会导致错误。

一般来说,删除传递给函数的指针是个坏主意。不能保证指针是 new 返回的指针,即使它是:by which new?

要为数组创建强引用计数器,您必须使用 std::shared_ptr<Block[]>,但通常最好使用某种容器。

在共享指针数组中查找指针对象的值:

auto it =  std::find_if( children.begin(),children.end(),[=](auto& el) { 
                return el.get() == block; 
           });

如果 children 不是静态的(那不是某种工厂),那么析构函数代码是完全多余的。在调用 ~Block() 之后,将调用 Block 的每个成员的析构函数,包括将释放其资源的集合和对每个指针调用析构函数的集合,它将对每个 Block 调用析构函数.

,

一旦一个指向 shared_ptr 的指针被赋予,shared_ptr 拥有它。您绝不能再次将该指针指向另一个共享指针。

int * ptr = new int(1234);
{
    std::shared_ptr<int> shp1(ptr); // shp1 owns ptr
    std::shared_ptr<int> shp2(ptr); // shp2 owns ptr ???
}
// at this point ptr has been deleted twice

注意:最好不要使用 new 而是调用 std::make_shared 并且永远不要首先将原始指针指向 shared_ptr。

你正在做的寻找孩子正是这个问题。传入一个原始指针,你会为它创建一个新的所有者。如果那个指针是一个孩子,它已经被拥有了。您不得在原始指针已被赋予共享指针后从原始指针创建共享指针。

此外,您为 Block 显示的析构函数没有错,但完全没有必要。您在那里编码的所有内容都会自动发生。块被销毁,它销毁它持有的向量(所以清除它是不必要的。)向量销毁它的元素,所以它持有的共享指针也被清理。