我们遇到了一些我们无法解释的问题,即使我们找到了解决方案,我也想知道为什么第一个代码很可疑。
这是一个最小的代码示例:
#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;
}
所以我想知道的是找不到的原因。
我有find_if的工作解决方案所以我真正想要的是理解为什么前两个不工作,而不是另一个工作解决方案(但如果你有一个更优雅的,请随时发布)。
答案 0 :(得分:2)
对于第一个,似乎shared_ptr没有比较 运算符与原始指针,有人可以解释为什么?
主观,但我当然不认为共享指针与原始指针相当是个好主意,我认为std::shared_ptr
的作者和标准委员会同意这种情绪。
第二个似乎是所有权问题,多次删除 (当我的本地shared_ptr超出范围时,它会删除我的指针),但是 我不明白的是为什么运行时错误是在查找期间 执行时,双重删除应仅在向量上发生 破坏,有什么想法吗?
s
是指向由int
作为块的一部分分配的make_shared
的指针,以及引用计数信息。它的实现定义了它实际上是如何分配的,但是你可以确定它不是一个简单的未修饰的新表达式,因为它会在它自己的内存位置分配一个单独的int。即它没有以这些方式分配:
p = new int;
p = new int(value);
p = new int{value};
然后您将s
传递给新shared_ptr
的构造函数(您作为[{1}}参数传递的shared_ptr
)。由于未将特殊删除器与指针一起传递,因此将使用默认删除器。默认删除器只会在指针上调用std::find
。
由于指针未分配了一个未经修饰的新表达式,因此在其上调用delete
是未定义的行为。由于临时delete
将在语句末尾被销毁,并且它认为它是整数的唯一所有者,因此将在语句末尾的整数上调用shared_ptr
。这可能是您的运行时错误的原因。
尝试以下操作,更容易理解代码段,您可能会遇到同样的问题:
delete
答案 1 :(得分:2)
智能指针类模板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
关于为什么在第一种情况下,因为它不仅仅是 value 的问题;它是一个等价的问题。 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以及move-construction或copy-construction,具体取决于是什么提供/合适的。结果是一个内存分配,它保持 参考数据和对象,后者是分配内存中的偏移量。因此,您发送到shared_ptr的指针不是来自分配返回值。
尝试第一个,我打赌你会看到你的运行时错误重新定位到向量的破坏而不是结束查找。
答案 2 :(得分:1)
bool operator ==(const std::shared_ptr<T>&, const T*)
不存在。这是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 ...