我知道std::shared_ptr
管理的对象不是delete
d reset()
,除非它是唯一在该点管理它的shared_ptr
。我知道当有多个shared_ptr
管理同一个对象时,对托管对象值的更改会反映在指向它的所有shared_ptr
中,而对这些shared_ptr
的任何更改都会发生变化由reset()
引起它的值(不其管理对象的值)(即将shared_ptr
从指向原始管理对象的对象更改为指向无对象的对象或其他东西)不会更改其他shared_ptr
s'值(即它们仍然指向原始托管对象,原始托管对象仍然存在):
#include <memory>
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<shared_ptr<int>> vec{ make_shared<int>(5) };
shared_ptr<int> sptr(vec[0]);
++ *sptr;
cout << *vec[0] << endl; // 6
vec[0].reset();
vec.pop_back();
cout << *sptr << endl; // 6
}
但是当使用两个级别的间接时,我失去了这种逻辑。给定一个名为Wrapper
和shared_ptr<shared_ptr<Wrapper>>
的类以及先前初始化的任意数量的其他shared_ptr<shared_ptr<Wrapper>>
,为什么此配置允许在任何内部{{1}上调用reset()
有效shared_ptr
所有其他内部reset()
?
我的猜测是:任何 out er shared_ptr
的托管对象是内部shared_ptr
(而不是shared_ptr
)并且更改为内部Wrapper
的值{shared_ptr
内部reset()
,它将内部shared_ptr
的值从指向shared_ptr
实例的值更改为在所有 out er Wrapper
中都会反映出一个无所不知的东西,有效地导致所有 out er shared_ptr
■失去对shared_ptr
实例的间接管理,从而删除Wrapper
实例。
但是同样的逻辑,是不是重置一个内部指针只会导致特定的内部指针失去对Wrapper
的管理?鉴于所有其他外部指针指向它们自己的内部指针(即用它们构造的那些),那些外部指针不会继续对Wrapper
进行间接管理,因为重置一个内部指针不会不要改变Wrapper
的值,其他内部指针仍然可以访问它?这对我来说是一个悖论。
如果重置一个内部指针有效地重置所有内部指针,那么这意味着内部指针&#39;在Wrapper
之前use_count()
1
为reset()
。我认为多个shared_ptr
可以出现来管理同一个对象同时将use_count()
保持在1
的唯一方法是通过幻觉:他们管理不同的对象(即具有相同值的不同地址的对象。我通过制作一个名为int
的{{1}}包装器进行了测试,其中唯一的数据成员是包裹的Wrapper
和int
static
,用于跟踪数量当前存在的instance_count
个实例。
Wrapper
显然有struct Wrapper {
Wrapper(int par = 0) : num(par) { ++instance_count; }
Wrapper(const Wrapper& src) : num(src.num) { ++instance_count; }
~Wrapper() { --instance_count; }
int num;
static int instance_count;
};
int Wrapper::instance_count = 0;
int main() {
shared_ptr<shared_ptr<Wrapper>> dual_ptr_1(
make_shared<shared_ptr<Wrapper>>(
make_shared<Wrapper>(Wrapper(5))
)
);
// - Output -
cout << Wrapper::instance_count << endl; // 1
shared_ptr<shared_ptr<Wrapper>> dual_ptr_2(dual_ptr_1);
cout << Wrapper::instance_count << endl; // 1
cout << dual_ptr_1->use_count() << endl; // 1
cout << dual_ptr_2->use_count() << endl; // 1
cout << dual_ptr_1.use_count() << endl; // 2
cout << dual_ptr_2.use_count() << endl; // 2
// note that above, the '->' operator accesses
// inner ptr while '.' operator is for outer ptr
cout << (*dual_ptr_1)->num << endl; // 5
cout << (*dual_ptr_2)->num << endl; // 5
dual_ptr_2->reset();
cout << Wrapper::instance_count << endl; // 0
cout << dual_ptr_1->use_count() << endl; // 0
cout << dual_ptr_2->use_count() << endl; // 0
cout << dual_ptr_1.use_count() << endl; // 2
cout << dual_ptr_2.use_count() << endl; // 2
}
个内部指针指向2
1
个对象;内心指针&#39; Wrapper
最多use_count
(销毁前); 1
班级Wrapper
最多为instance_count
(销毁前);并且可以通过两个外部指针访问间接管理的1
对象(这意味着外部指针都没有被另一个指针移动构造);并重置一个内部指针有效地重置所有这些;所以我仍然不理解看似悖论。
我也在这篇文章中提出了相同的问题,其中上述代码将内部Wrapper
替换为shared_ptr
s,内部unique_ptr
被替换由make_shared
和make_unique
注释掉内部指针(因为use_count()
缺少该方法),它提供相同的输出。这对我来说似乎是一个悖论,因为unique_ptr
在这里看起来并不独特。
答案 0 :(得分:3)
给定一个名为
Wrapper
和shared_ptr<shared_ptr<Wrapper>>
的类 任何数量的其他shared_ptr<shared_ptr<Wrapper>>
s初始化为 之前,为什么这个配置允许reset()
调用任何 内部shared_ptr有效reset()
所有其他内部shared_ptrs
?
没有其他内部shared_ptr
,你有一个包含对象的实例,即
dual_ptr_1
\
--> shared_ptr --> Wrapper
/
dual_ptr_2
而不是
dual_ptr_1 --> shared_ptr
\
--> Wrapper
/
dual_ptr_2 --> shared_ptr
致电dual_ptr_2->reset();
后,此更改为
dual_ptr_1
\
--> shared_ptr --> (empty)
/
dual_ptr_2
答案 1 :(得分:1)
接受的答案用图表显示OP代码中发生的情况:两个外部shared_ptr
指向同一内部shared_ptr
,指向Wrapper
对象。 (我参考接受答案的未经编辑的版本中的图表;在我的回答时尚未对其进行编辑。)接受的答案有另一个图表,其中显示了OP预期会发生但未发生的情况,我称之为:
案例A - 两个指向不同内部指针的外部指针,指向同一个Wrapper
对象(请参阅图表的已接受答案)。
以下是导致案例A的代码:
int main() {
shared_ptr<shared_ptr<Wrapper>> dual_ptr_1(
make_shared<shared_ptr<Wrapper>>(make_shared<Wrapper>(5))
);
shared_ptr<shared_ptr<Wrapper>> dual_ptr_2(
make_shared<shared_ptr<Wrapper>>(*dual_ptr_1)
);
cout << dual_ptr_1.use_count() << endl; // 1
cout << dual_ptr_2.use_count() << endl; // 1
cout << dual_ptr_1->use_count() << endl; // 2
cout << dual_ptr_2->use_count() << endl; // 2
}
我将dual_ptr_1
称为第一个外部指针,并将shared_ptr
称为第一个内部指针。我将dual_ptr_2
称为第二个外部指针,将shared_ptr
称为第二个内部指针。两个外部指针指向不同的内部指针。第二个外部指针未复制构造或分配给第一个外部指针,因此外部指针的use_count
为1
。第二个外部指针不指向第一个内部指针,而是指向从第一个内部指针复制构造的无名内部指针。虽然第二个外部指针仍在管理第二个内部指针,但后者的无名称不会导致后者超出范围。第二个内部指针指向与第一个内部指针相同的Wrapper
,因为第二个内部指针是从第一个内部指针复制构造的。由于此shared_ptr
复制构造,内部指针的use_count
为2
。每个内部指针必须为reset()
为零或其他东西,分配给其他东西,或超出范围以便Wrapper
被销毁(内部指针不需要经过相同的操作,只要每个操作至少经历其中一个)。
这是另一个,案例B - 与案例A相同的图表,但是有一个错误的实现和不同的控制台输出:
int main() {
shared_ptr<shared_ptr<Wrapper>> dual_ptr_1(
make_shared<shared_ptr<Wrapper>>(make_shared<Wrapper>(5))
);
shared_ptr<shared_ptr<Wrapper>> dual_ptr_2(
make_shared<shared_ptr<Wrapper>>(&(**dual_ptr_1))
); // (*)
cout << dual_ptr_1.use_count() << endl; // 1
cout << dual_ptr_2.use_count() << endl; // 1
cout << dual_ptr_1->use_count() << endl; // 1
cout << dual_ptr_2->use_count() << endl; // 1
} // <- Double free runtime error at closing brace.
// Replacing line (*) with:
// shared_ptr<shared_ptr<Wrapper>> dual_ptr_2(
// new shared_ptr<Wrapper>(&(**dual_ptr_1))
// );
// has much the same effect, possibly just without compiler optimization.
案例B是案例A的错误变体,其中案例B的区别在于第二个内部指针是从指向Wrapper
对象的原始指针构建的,而不是从第一个内部复制构造或分配指针。因此,内部指针的use_count
保持在1
(而不是2
),即使它们都指向同一地址。因此,这些内部指针中的每一个都表现得好像它是唯一一个管理Wrapper
对象的指针。在main()
的右括号中发生双重自由运行时错误,因为最后一个超出范围的内部指针试图释放已经被前一个内部指针超出范围释放的内存。
这里案例C - 两个外部指针指向指向不同Wrapper
个对象的不同内部指针:
int main() {
shared_ptr<shared_ptr<Wrapper>> dual_ptr_1(
make_shared<shared_ptr<Wrapper>>(make_shared<Wrapper>(5))
);
shared_ptr<shared_ptr<Wrapper>> dual_ptr_2(
make_shared<shared_ptr<Wrapper>>(make_shared<Wrapper>(**dual_ptr_1))
);
cout << dual_ptr_1.use_count() << endl; // 1
cout << dual_ptr_2.use_count() << endl; // 1
cout << dual_ptr_1->use_count() << endl; // 1
cout << dual_ptr_2->use_count() << endl; // 1
}
虽然Wrapper
个对象具有相同的值,但它们是不同的对象,并且它们位于不同的地址。第二个内部指针的Wrapper
对象是从第一个内部指针的Wrapper
对象复制而成。