2014-11-05 289 views
1

我知道由std::shared_ptr管理的对象不是delete d由reset(),除非它是管理它在该点的唯一shared_ptr。我知道当有多个shared_ptr管理同一个对象时,对被管理对象值的更改通过指向它的所有shared_ptr s反映出来,而对这些shared_ptr的值(而不是其管理对象的值)通过reset()婷它引起的(即改变shared_ptr从一个指向原来的管理对象,以一个指向什么或别的东西),不改变其他shared_ptr S'值(即它们都依然指向原来的管理对象,和原来的管理对象仍然存在):重置一个shared_ptr的嵌套智能指针到一个shared_ptr(或到的unique_ptr),看似矛盾

#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 
} 

但是,逻辑是使用两个层次:○当我失去了联系f间接。给定一个名为Wrapper类和shared_ptr<shared_ptr<Wrapper>>并初始化为任意数量的其他shared_ptr<shared_ptr<Wrapper>> S的前,为什么会发生这种配置允许reset()呼吁任何内部shared_ptr有效reset()所有其他内shared_ptr S'

我的猜测是:任何 ER的shared_ptr S中的管理对象是内shared_ptr(不是Wrapper)和改变的值的内shared_ptr(由reset()婷内shared_ptr,其改变从一个指向一个Wrapper实例,一个指向任何内部shared_ptr的值)在所有shared_ptr S反射,有效地引起所有shared_ptr结束了在失去间接管理实例,从而删除Wrapper实例。

但是,同样的逻辑,是不是复位内指针只会导致特定的内部指针在Wrapper失去管理的一个?由于所有其他外部指针指向(构建与他们,即那些)自身的内部指针,将不是那些外的人继续在Wrapper有间接管理,因为重置一个内部指针不改变Wrapper的价值,这应该仍然可以访问由其他内部指针?这对我来说是一个悖论。

如果重置一个内部指针有效地重置所有的内部指针,那么意味着内部指针'use_count()1正好在reset()之前。只有这样,我想多shared_ptr S能出现,同时在1保持use_count()将通过错觉管理相同的对象:他们管理不同的对象具有相同的值(在不同的地址,即对象)。我通过制作一个名称为Wrapperint包装来测试此功能,该包装的唯一数据成员是包装的intstaticinstance_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 
} 

显然有指向1Wrapper对象2内的指针;内部指针'use_count最多为1(销毁前); Wrapper类的instance_count最多为1(破坏前);并且间接管理的对象可以通过两个外部指针访问(这意味着两个外部指针都不会被其他指针移动构建)。并重置一个内部指针有效地重置所有这些;所以我仍然不明白这似乎是悖论。

我还问在这个岗位约在上面的代码有内shared_ptr S按unique_ptr条取代的情况下同样的问题,内make_shared换成make_uniqueuse_count()注释掉的内部指针(因为unique_ptr缺少该方法),它给出相同的输出。这对我来说似乎是一个悖论,因为unique_ptr在这里似乎并不独特。

+1

我不确定你的期望是什么,'dual_ptr_1'和'dual_ptr_2'都共享单个内部'shared_ptr'的所有权,而后者又管理'Wrapper'的单个实例。 'dual_ptr_2-> reset();'销毁单个'Wrapper'实例,这样你就可以共享一个空的'shared_ptr'了。 – user657267 2014-11-05 02:30:42

+0

正如其他人所说的。可以这样想:'Wrapper * w = new Wrapper();包装** a =&w;包装** b =&w;删除* a; * a = nullptr;'< - 您会惊讶现在'* b'现在为空吗? – cdhowie 2014-11-05 03:31:08

回答

3

提供类名为Wrappershared_ptr<shared_ptr<Wrapper>>和 初始化 任意数量的其他shared_ptr<shared_ptr<Wrapper>> S的前,为什么会发生这种配置允许reset()呼吁任何 内的shared_ptr有效reset()所有其他内shared_ptrs

有没有其他内shared_ptr S,你有包含的对象的单个实例,即

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

接受的答案显示了OP代码中发生的情况:两个外部shared_ptr s指向相同的内部shared_ptr,它指向Wrapper对象。 (我指的是接受答案的未编辑版本中的图表;它在我的答案时没有被编辑过)。接受的答案有另一个图表,显示了OP预期发生但未发生的情况,我将其称为:

案例A - 两个指向不同内部指针指向相同对象的指针(请参阅图的接受答案)。

这里的代码使得情况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_count1。第二个外部指针不指向第一个内部指针,而是指向从第一个内部指针复制构建的无名内部指针。虽然第二个外部指针仍在管理第二个内部指针,但后者的无名称不会导致后者超出范围。第二个内部指针指向与第一个内部指针相同的Wrapper,因为第二个内部指针是从第一个内部指针复制构建的。由于此shared_ptr复制构造,内指针的use_count2。每个内部指针必须为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),即使它们都指向相同的地址。所以,这些内部指针的每个行为就好像它是唯一一个管理对象的行为。在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构建的副本。