共享指针的c ++向量。如果在向量之外转换,它会改变向量中的指针吗?

时间:2013-05-15 07:42:49

标签: c++ c++11 vector casting shared-ptr

我有一个基类Base和2个派生类Child_A和Child_B。 当一个对象My_Object被实例化(作为共享指针)时,我不知道它是Child_A或Child_B。所以它被实例化为Base。 我将My_Object推送到Base类型的共享指针的std :: vector中。 后来当我知道My_Object属于哪个派生类时,我在My_Object上使用.reset()将其强制转换为派生类Child_A。 我的问题是,向量中的My_Object是否也会被强制转换为Child_A? 如果没有,我该怎么办? 谢谢!

编辑:

obj_array.push_back(std::shared_ptr<Base>(new Base());
container.push_back(obj_array[0]]);
obj_array[0].reset(Child_A());

容器[0]会被转换为Child_A吗? 如何将其投射到Child_A?

编辑以进一步澄清申请: 我想我的obj_array的评论可以是shared_ptr的一个向量落入我的应用程序,我希望有一个obj_array的主容器&gt;拥有所有对象。然后我有几个从属容器Container1 Container2 ...来保存一些对象。我希望通过修改主容器来控制任何对象,并将效果广播到所有从属容器。在这个应用程序中,我想我可能只需要一个<shared_ptr<Base>>的主矢量,并且有几个从属矢量<shared_ptr<shared_ptr<Base>>><shared_ptr<Base>*>

1 个答案:

答案 0 :(得分:1)

我刚刚注意到您添加到问题中的代码。 SharedPointers不会这样做。

SharedPointers是&#34;共享&#34;在&#39;引用计数&#39;而不是&#34;共享&#34;在&#34;数据共享&#34;方面。 &#34;数据共享&#34;用你的术语来完成..指针。无论你想要什么&#34;分享&#34;,用指针来指代它。然后,如果你改变它,每个人都会看到更新。但你必须改变,而不是指向它的指针

也就是说,在这里,您想要更新指针,并希望每个人都看到指针正在更新。因此,您必须通过指针保持指针。

也就是说,不要保留vector<shared_ptr<Base>>,而是保留vector<shared_pointer<Base>*>

现在,当你想&#34;全球取代&#34;使用新实例的对象,将指针所持有的shptr<Base>替换为另一个新的shptr。

如果你不喜欢原始指针,你甚至可以使用vector<shared_pointer<sahred_pointer<Base>>和.reset内部,同时保持外部不受影响。每个得到外部副本的人都会看到内部副本的更新。

// obj_array and container are a vector<shared_ptr<Base>*>
// or a vector<shared_ptr<shared_ptr<Base>>>

obj_array.push_back(new std::shared_ptr<Base>(new Base()); // note the 'new'
container.push_back(obj_array[0]]);

(*obj_array[0]) .reset(Child_A()); // note the '*'

obj_array[0] -> reset(Child_A()); // or, just in short

编辑#2:

在您的评论之后&#34;另外一点,obj_array不必是指向指针的向量。它可以只是一个vector<shared_ptr<Base>>。如果我错了,请纠正我:

这取决于你想要达到的目标。无论如何,你可以将内容保存在矢量中 - 它只会影响它们使用的某些风格。现在让我描述一个非常抽象的设置。你有一个主容器,以某种方式持有一些东西。应用程序中称为A,B,C的部分会定期执行这些操作并对其执行某些操作。 B部分和C部分有时会出于某种目的在内部记住事物。现在假设:

  • 案例1:vector包含Base*个对象,
  • 案例2:vector包含shared_ptr<Base>个对象,
  • 案例3:vector持有Base **
  • 案例4:vector持有shptr<shptr<Base>>

当然有更多可能的情况,但让我们修剪它。现在,您的应用程序正在运行,并且主容器中已有一些对象。模块A,B,C已经处理了一些东西,可能模块B和C已经记住了一些对象。现在应用程序达到了需要将主容器中的第5项替换为new Bar()的程度。

案例1:

向量是Base*。 Bar当然会实现Base,因此新的Bar()可以直接分配给vector[4]。当然,您已决定如何处理旧元素。删除还是忘记?然后,执行vector[4]=new Bar(),从现在开始,读取此主向量的每个人都将在第5个位置看到新对象。

但是,这不是问题的结束:模块B和C可能仍然知道旧对象。由于向量元素是Base *(原始指针),那些B / C已经复制了指向旧对象的原始值,所以现在唯一的解决方案是明确地告诉B和C也执行取代

因此,案例1结束如下面的代码。它分为三个阶段:初始设置示例,运行时操作示例和最终清理示例。

vector<Base*> vector;
vector.resize( 10 );

///// .... later ....

Base* olditem = vector[ 4 ];
Base* newitem = new Bar();

bool iWillDeleteTheOld = well_somehow_decide();

vector[4] = newitem;
moduleB->updateAfterReplace(olditem, newitem,  iWillDeleteTheOld);
modulec->updateAfterReplace(olditem, newitem,  iWillDeleteTheOld);

if(iWillDeleteTheOld)
   delete olditem;

///// .... later ....

for( ... idx ...)
    delete vector[idx];

vector.resize(0);

modulesB / C将矢量项读取为Base*,如果缓存它 - 它们将其缓存为Base*

请注意,这段代码会让您在每个&#39;模块中编写其他功能updateAfterReplace。这仍然可以记住旧物体。并且你需要协调他们的内部updateAfterReplace 永远不要试图删除旧的对象,以防你要在这里做,或者你在这里将双重删除它并严重崩溃。我通过告诉他们iWillDeleteTheOld来解决这个问题。如果他们知道我会这样做,他们将跳过旧的对象删除阶段。但是,如果我决定不删除它(iwilldelete = false),他们仍然可以决定自行删除它。

但是,这只是一个适当的所有权管理问题,我们不在这里谈论它。

案例2:

向量是shared_ptr<Base>。 Bar当然会实现Base,所以新的Bar()可以直接赋值给vector[4](无变化)。当然,您已决定如何处理旧元素(无更改)。

更改)但是,由于您将指针保持为shared_ptr,因此所有权没有问题:您只是覆盖/释放指针,shptr将处理其余的事情。如果有人使用该对象,它将删除它。如果仍然使用它,它将保留它。

然后,执行vector[4]=new Bar(),从现在开始,读取此主向量的每个人都将在第5个位置看到新对象。 (没有变化)

但是,这不是问题的结束:模块B和C可能仍然知道旧对象。由于向量的元素是sharedptr<Base>,那些B / C已经复制了shared_ptr到old-object,所以现在唯一的解决方案是明确告诉B和C也执行replace。 (无变化

因此,案例2结束为

vector<shared_ptr<Base>> vector;
vector.resize( 10 );

///// .... later ....

sharedptr<Base> olditem = vector[4];
sharedptr<Base> newitem = new Bar();

vector[4].reset( newitem ); // <- THE LINE

moduleB->updateAfterReplace(olditem, newitem);
modulec->updateAfterReplace(olditem, newitem);

///// .... later ....

vector.resize(0);

modulesB / C将矢量项读取为sharedptr<Base>,如果缓存它 - 它们将其缓存为sharedptr<Base>。任何减少到Base*都会将案例转换为Case1。

请注意&#39;决定删除&#39;和&#39;对象删除&#39;并且&#39;我告诉你那个我删除它is gone. This is the benefit of sharedptr`。但是,我仍然需要手动更新可能仍然保留旧对象的所有其他缓存

这是因为THE LINE不仅会覆盖shared_ptr,还会删除&#39;阶段:将shared_ptr与内部引用计数机制分离,如果计数降为零 - 删除对象。问题出在这里:它分离了。 已经从向量[4]中的shptr复制的所有其他sharedptr现在正在形成他们自己的引用计数组,他们仍然记得旧对象。他们没有更新内容。他们只是从refcount = 3集体下降到refcount = 2。

案例3:

向量是Base**。 Bar当然实现了Base,所以新的Bar()不能直接赋值给vector[4]:vector现在拥有指向指针的指针,因此还需要额外的解引用(更改) 。当然,您已决定如何处理旧元素(无更改)。删除还是忘记? (没有变化)

然后,执行*vector[4]=new Bar(),从现在开始,读取此主向量的每个人都将在第5个位置看到新对象。 (没有变化)

这就是问题的结束。 (更改

因此,案例3结束为

vector<Base**> vector;
for(int i = 0; i<10; ++i)
   vector.push( new Base* );

///// .... later ....

Base* olditem = * vector[4]; // note the dereference
Base* newitem = new Bar();

bool iWillDeleteTheOld = well_somehow_decide();

* vector[4] = newitem; // note the dereference

if(iWillDeleteTheOld)
   delete olditem;

///// .... later ....

for( ... idx ...)
{
    delete * vector[idx];  // delete the object
    delete vector[idx]; // delete the pointer
}

vector.resize(0);

modulesB / C将矢量项读取为Base**,如果缓存它 - 它们将其缓存为Base**。任何减少到Base*都会将案例转换为Case1。

首先请注意,现在您的矢量必须使用指针完全初始化。它不需要像那样完成,你可以动态地完成它,但是在两条线路上都可以做到这一点,并注意取消引用&#34;你必须绝对确定在vector [nth]有一个正确分配的pointer-to-Base*(所以,Base **,所以new Base*)。

由于向量的元素现在是Base**,这些B / C模块可能已经复制了Base** - 而不是Base*!所以,用自然语言说,模块B / C现在记住指针所在的位置而不是对象所在的位置。如果你将指针更改为指向其他地方,它们会立即看到它,因为它们首先查看地点指针所在的位置,然后它们会找到指针的新版本。

这样,级联更新消失了,对象所有权/删除也被简化了,但仍然存在。但!另外,出现了新的碎片:因为你必须分配额外的指针,你还必须在某些时候删除它们。

案例4:

vector<sharedptr<sharedptr<Base>>> vector;
for(int i = 0; i<10; ++i)
   vector.push( new sharedptr<Base> );

///// .... later ....

(* vector[4] ).reset( new Bar() ); // note the dereference

///// .... later ....

vector.resize(0);

modulesB / C将矢量项读取为sharedptr<sharedptr<Base>>,如果缓存它 - 它们将其缓存为sharedptr<sharedptr<Base>>。对Base*sharedptr<Base>的任何减少都会将案例分别转换为Case1或Case2。

我希望你现在能得到所有的分歧,所以我不会重复这里发生的事情的细节。


免责声明:所有示例都只是解释性的。这些代码都没有经过测试,这些代码都没有#34;完成&#34;。例如,有很多错误检查丢失了。它们只是为了展示机制的骨架。由于拼写错误等原因,他们甚至可能无法编译。

最后一句话#1:当然,你可以自由地混合指针和共享指针。您可以使用shared_ptr<shared_ptr<Base>>shared_ptr<Base*>shared_ptr<Base>*,而不是Base**。它只影响初始化和清理。不是替换更新问题。

最后的单词#2:请注意,还有引用。如果模块B / C通过引用捕获了Base*shared_ptr<Base> ,那么引入Base**shared_ptr<shared_ptr<Base>>.就没有意义了。按引用捕获已经引入了所需的相同的一个更多级别的间接。事实上,参考&amp;内部只是一个原始指针,因此&-to-Base*实际上是Base**

最后一句话#3:

我已经说过了,但让我再说一遍。核心问题是shared_ptr已经分享了#39}就引用计数而言,而不是数据。因此:

shared_ptr<Foo> first = new Foo(1);
shared_ptr<Foo> second = first;
shared_ptr<Foo> third = second;

// now first == Foo#1   \
// now second == Foo#1  | refcount = 3
// now third == Foo#1   /

third.reset( new Bar(2) );

// now first == Foo#1   \ refcount = 2
// now second == Foo#1  /
// now third == Bar#2   - refcount = 1

second.reset( new Asdf(3) );

// now first == Foo#1   - refcount = 1
// now second == Asdf#3 - refcount = 1
// now third == Bar#2   - refcount = 1

first.reset( third );

// now first == Bar#2                   \
// now second == Asdf#3 - refcount = 1  | - refcount = 2
// now third == Bar#2                   /
// and Foo#1 gets deleted