c++的共享指针向量.如果在vector外强制转换,它会改变vector中的指针吗?< / h1 >

c++ vector of shared pointer. If casted outside of vector, will it change the pointer in vector?

本文关键字:vector 指针 改变 lt gt h1 如果 向量 共享 c++ 转换      更新时间:2023-10-16

我有一个基类base和两个派生类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>持有所有的对象。然后我有几个从属容器Container1 Container2…容纳一些物体。我想有一个主控制的任何对象通过修改主容器和效果广播到所有的从属容器。在这个应用程序中,我想我可能只需要一个<shared_ptr<Base>>的主向量,并有几个<shared_ptr<shared_ptr<Base>>><shared_ptr<Base>*>的从向量。

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

sharedpointer在"引用计数"方面是"共享的",而不是在"数据共享"方面是"共享的"。你们所说的"数据共享"是通过……指针。无论您想要"共享"什么,都可以通过指针来引用它。然后,如果您更改它,每个人都会看到更新。但是你必须把改成,而不是指向它的指针.

也就是说,在这里,您希望更新指针,并且希望每个人都看到指针被更新。因此,必须一个指针一个指针地保存。

即不保留vector<shared_ptr<Base>>,保留vector<shared_pointer<Base>*>

现在,当你想用新的实例"全局替换"对象时,你可以用另一个新的shptr替换指针持有的shptr<Base>

如果你不喜欢原始指针,你甚至可以使用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:

在你的评论"另外一点,obj_array不一定是指针到指针的向量。它可以只是一个vector<shared_ptr<Base>>。如果我说错了,请纠正我":

这取决于你想要达到什么目标。你可以用任何你喜欢的方式保持矢量——它只会影响它们的某些使用场景。现在让我描述一个非常抽象的设置。你有一个主容器来存放一些东西。你的应用程序中称为A B C的部分会周期性地获取这些东西并对它们执行一些操作。B部分和C部分有时倾向于出于某种目的在内心记住一些东西。现在假设:

  • case 1: vector保存Base*对象,
  • 情况2:vector保存shared_ptr<Base>对象,
  • case 3: vector保持Base**
  • 情形4:向量保持shptr<shptr<Base>>

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

案例1:

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

但是,问题还没有结束:模块B和C可能仍然知道旧对象。由于vector的元素是Base*(原始指针),这些B/C已经复制了指向旧对象的指针的原始值,因此现在唯一的解决方案是显式地告诉B和C也执行replace。

因此,情况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);

模块esb/C读取向量项为Base*,如果缓存它,它们将其缓存为Base*

请注意,这段代码导致您在每个可能仍然记住旧对象的"模块"中编写额外的函数updateAfterReplace。而你需要编排它们的内部updateAfterReplaceto不试图删除旧的对象,以防你在这里做,否则你会双重删除它,并严重崩溃。我在这里通过告诉他们iWillDeleteTheOld是否。如果它们知道我会这样做,它们就会跳过旧的对象删除阶段。然而,如果我决定不删除它(iwilldelete=false),他们仍然可能决定自行删除…

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

案例2:

向量属于shared_ptr<Base>。Bar当然实现了Base,所以new Bar()可以直接赋值给vector[4](没有变化)。当然,您必须决定如何处理旧元素(没有更改)。

()但是,由于您将指针保存为shared_ptr,因此所有权没有问题:您只是覆盖/释放指针和SHPTR将负责其余的工作。如果有人使用该对象,它将删除它。如果它仍在使用,它将保留它。

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

但是,问题还没有结束:模块B和C可能仍然知道旧对象。由于vector的元素是sharedptr<Base>,这些B/C已经将shared_ptr复制到old-object,因此现在唯一的解决方案是显式地告诉B和C也执行replace。(没有变化)

因此,case 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);

模块esb/C读取向量项为sharedptr<Base>,如果缓存它,它们将其缓存为sharedptr<Base>。任何对Base*的还原都会将case转换为Case1。

请注意"决定删除"answers"对象删除"以及"我告诉你,我删除它is gone. This is the benefit ofsharedptr"。但是,我仍然需要手动更新可能仍然保存旧对象的所有其他缓存.

这是因为THE LINE不仅覆盖了shared_ptr,而且还执行了'maybe delete'阶段:将shared_ptr从内部引用计数机制中分离出来,如果计数降为零-删除对象。问题就在这里:它会脱落。的所有sharedptr都是从复制过来的vector[4]中的SHPTR现在形成了自己的重计数组,它们仍然记住旧对象。他们没有更新内容。它们只是从refcount=3下降到refcount=2。

案例3:

向量属于Base**。Bar当然实现了Base,所以new Bar()不能直接赋值给vector[4]: vector现在保存一个指针到指针,所以还需要一个额外的解引用(change))。当然,您必须决定如何处理旧元素(没有更改)。删除还是忘记?(没有变化)

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

问题到此结束。()

因此,case 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);

模块esb/C读取向量项为Base**,如果缓存它,它们将其缓存为Base**。任何对Base*的还原都会将case转换为Case1。

首先,注意现在vector必须完全初始化与指针。不需要这样做,您可以在运行中这样做,但是在"注意解引用"的两行中,您必须绝对确定在vector[n]处有一个正确分配的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);

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

我希望你现在已经了解了所有的不同之处,所以我就不重复这里发生的细节了。


免责声明:所有示例只是解释性的。这些代码都没有经过测试,这些代码都不是"完整的"。例如,有很多错误检查缺失。它们只是用来展示机制的骨架。它们甚至可能由于拼写错误等原因而无法编译。

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

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

结束语#3:

我已经说过了,但让我再说一遍。核心问题是shared_ptr在引用计数方面是"共享"的,而不是数据。因此:
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