繁体   English   中英

为什么std :: find在std :: shared_ptr上不起作用

[英]Why std::find doesn't work on std::shared_ptr

我们遇到了一些我们在工作中无法解释的事情,即使我们找到了解决方案,我也想确切地知道为什么第一个代码很烂。

这是一个最小的代码示例:

#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>

int main() {

    std::vector<std::shared_ptr<int>> r;

    r.push_back(std::make_shared<int>(42));
    r.push_back(std::make_shared<int>(1337));
    r.push_back(std::make_shared<int>(13));
    r.push_back(std::make_shared<int>(37));

    int* s = r.back().get();

    auto it = std::find(r.begin(),r.end(),s); // 1 - compliation error
    auto it = std::find(r.begin(),r.end(),std::shared_ptr<int>(s)); // 2 - runtime error
    auto it = std::find_if(r.begin(),r.end(),[s](std::shared_ptr<int> i){
    return i.get() == s;}
    ); // 3 -works fine

    if(it == r.end())
        cout << "oups" << endl;
    else
        cout << "found" << endl;    

    return 0;
}

所以我想知道的是为什么这个发现不起作用。

  1. 对于第一个,似乎shared_ptr没有带有原始指针的比较运算符 ,有人可以解释为什么吗?
  2. 第二个似乎是所有权问题,多次删除(当我的本地shared_ptr超出范围时,它删除了我的指针),但是我不明白的是为什么运行时错误是在查找执行期间发生的,因此两次删除应该仅在媒介破坏上发生,有什么想法吗?

我有find_if的有效解决方案,所以我真正想要的是理解为什么前两个不起作用,而不是另一个有效解决方案(但是如果您有一个更优雅的解决方案,请随时发布)。

对于第一个,似乎shared_ptr没有带有原始指针的比较运算符,有人可以解释为什么吗?

主观的,但我当然不认为共享指针与原始指针具有可比性是一个好主意,我认为std::shared_ptr的作者和标准委员会对此观点表示赞同。

第二个似乎是所有权问题,多次删除(当我的本地shared_ptr超出范围时,它删除了我的指针),但是我不明白的是为什么运行时错误是在查找执行期间发生的,因此两次删除应该仅在媒介破坏上发生,有什么想法吗?

s是指向由make_shared作为块的一部分分配的int的指针,以及引用计数信息。 它的实现定义了它的实际分配方式,但是您可以确保它没有使用简单的未经修饰的新表达式,因为那样会在其自己的内存位置分配一个单独的int值。 也就是说,它没有通过以下任何一种方式进行分配:

p = new int;
p = new int(value);
p = new int{value};

然后,将s传递给新的shared_ptr的构造函数(作为参数传递给std::findshared_ptr )。 由于没有将特殊的删除器与指针一起传递,因此将使用默认的删除器。 默认的删除器将只对指针调用delete

由于没有为指针分配未经修饰的新表达式,因此对它调用delete是未定义的行为。 由于临时shared_ptr将在语句末尾销毁,并且认为它是整数的唯一所有者,因此将在语句末尾对整数调用delete 这可能是您的运行时错误的原因。

尝试以下操作,更容易推断出代码段,您可能会遇到相同的问题:

auto p = std::make_shared<int>(10);
delete p.get();  // This will most likely cause the same error.
                 // It is undefined behavior though, so there
                 // are no guarantees on that.

智能指针类模板std::shared_ptr<>仅支持将运算符与其他std::shared_ptr<>对象进行比较; 不是原始指针。 具体来说,在这种情况下支持以下功能:

operator==    - Equivalence
operator!=    - Negated equivalence
operator<     - Less-than
operator<=    - Less-than or equivalent
operator>     - Greater-than
operator>=    - Greater-than or equivalent

在这里阅读更多信息

至于为什么在第一种情况下,因为它不只是一个价值问题; 这是一个对等的问题。 仅仅因为该原始地址可能保存在共享指针中,所以不能认为std::shared_ptr<>与原始地址等效或相当。 即使地址等价的,也并不意味着后者的来源就来自经过适当引用计数的等效项(即另一个共享指针)。 有趣的是,您的第二个示例展示了尝试装配该系统时发生的情况。

关于第二种情况,按原样构造共享指针将声明两个具有相同动态资源独立所有权的独立共享指针。 因此,问问自己,哪个人可以删除它? 嗯...是的 仅当您复制std::shared_ptr<>本身时,才可以正确管理在具有相同基准引用的共享指针之间共享的引用计数材料,因此在这种情况下,您的代码是普通错误的

如果你想共享指针的集合狩猎原始地址下来,你的第三个方法正是你应该怎么做。

编辑:为什么情况2的所有权问题会在哪里呈现呢?

好的,我做了一些狩猎,结果发现它是一个运行时的东西(至少在我的实现中)。 我将必须检查以确定该行为( std::make_shared行为)是否在标准中得到了加强,但我对此表示怀疑。 底线是这个。 这两件事:

r.push_back(new int(42));

r.push_back(std::make_shared<int>(42));

可以做非常不同的事情。 前者动态分配一个新的int ,然后将其地址发送给匹配的std::shared_ptr<iint>构造函数,后者构造自己的共享引用数据,该数据用于管理对提供地址的引用计数。 也就是说,有来自不同分配的两个不同的数据块。

但是后者的功能有所不同。 它使用同一对象的placement-new方法在同一内存块中分配对象共享参考数据,并根据提供的内容/适当的内容使用移动构造或复制构造。 其结果是有一个存储器分配,它拥有两个基准数据对象,所述分配存储器内后者的偏移量。 因此,您要发送到shared_ptr的指针不是来自分配返回值。

尝试第一个,我敢打赌,您会看到您在运行时错误中重新定位为破坏向量,而不是查找结论。

  1. bool operator ==(const std::shared_ptr<T>&, const T*)不存在。
  2. std::shared_ptr用法很糟糕,就像您做的那样:

     int* p = new int(42); std::shared_ptr<int> sp1(p); std::shared_ptr<int> sp2(p); // Incorrect, should be sp2(sp1) // each sp1 and sp2 will delete p at end of scope -> double delete ... 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM