0

我有一个基类 Base 和 2 个派生类 Child_A 和 Child_B。当对象 My_Object 被实例化(作为共享指针)时,我不知道它是 Child_A 还是 Child_B。所以它被实例化为Base。我将 My_Object 推入基类型共享指针的 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... 来保存一些对象。我想通过修改主容器来对任何对象进行主控制,并将效果广播到所有从容器。在这个应用程序中,我想我可能只需要 的 Master 向量<shared_ptr<Base>>,并有几个<shared_ptr<shared_ptr<Base>>>或的 Slave 向量<shared_ptr<Base>*>

4

1 回答 1

1

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

SharedPointers 在“引用计数”方面是“共享的”,而不是在“数据共享”方面是“共享的”。用您的话来说,“数据共享”是由 .. 指针完成的。无论你想“分享”什么,指针指向它。然后,如果您更改它,每个人都会看到更新。但是你必须改变,而不是指向它的指针。

也就是说,在这里,您要更新指针,并且希望每个人都看到正在更新的指针。因此,您必须逐个指针地持有指针。

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

现在,当您想用新实例“全局替换”对象时,您可以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:

在您发表评论“还有一点,obj_array 不必是指向指针的向量。它可以只是一个vector<shared_ptr<Base>>。如果我错了,请纠正我”:

这取决于你想达到什么目标。您可以随心所欲地将事物保存在向量中 - 它只会影响其使用的某些场景。现在让我描述一个非常抽象的设置。你有一个主容器,它以某种方式容纳一些东西。您的应用程序中称为 A、B、C 的部分会定期获取这些内容并对其执行某些操作。B 部分和 C 部分有时会出于某种目的而在内部记住事物。现在假设:

  • 案例 1:vector 持有Base*对象,
  • 案例 2:vector 持有shared_ptr<Base>对象,
  • 案例 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 可能仍然知道旧对象。由于向量的元素是 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*.

请注意,这段代码会导致您updateAfterReplace在每个“模块”中编写附加函数,这些“模块”可能仍会记住旧对象。而且您需要协调它们的内部updateAfterReplace,以免在您要在这里执行的情况下尝试删除旧对象,否则您将在这里双重删除它并严重崩溃。我在这里通过告诉他们是否iWillDeleteTheOld. 如果他们知道我会这样做,他们将跳过旧的对象删除阶段。但是,如果我决定不删除它(iwilldelete=false),他们仍然可能决定自行删除它。

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

案例二:

向量是shared_ptr<Base>。Bar 当然实现了 Base,所以 new Bar() 可以直接分配给vector[4](没有变化)。当然,您已经决定如何处理旧元素(没有变化)。

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

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

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

因此,案例 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*都会将 case 转换为 Case1。

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

这是因为 THE LINE 不仅覆盖了 shared_ptr,而且还执行“可能删除”阶段:将 shared_ptr 从内部引用计数机制中分离出来,如果计数下降到零 - 删除对象。问题就在这里:它分离了。从vector[4] 中的 shptr复制的所有其他sharedptr内容现在正在形成自己的引用计数组,并且它们仍然记得旧对象。他们没有更新内容。他们只是集体从 refcount=3 下降到 refcount=2。

案例3:

向量是Base**。Bar 当然实现了 Base,因此 new Bar() 不能直接分配给vector[4]: vector 现在拥有一个指向指针的指针,因此还需要额外的取消引用(change)。当然,您已经决定如何处理旧元素(没有变化)。删除还是忘记?(没变)

然后,*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*都会将 case 转换为 Case1。

首先,请注意,现在您的向量必须用指针完全初始化。不需要那样做,你可以即时做,但在这两行“注意取消引用”,你必须绝对确定在 vector[nth] 有一个正确分配的pointer-to-Base*(所以,Base** ,所以new Base*)。

由于vector的元素现在是Base**,那些B/C模块可能已经复制了Base**-not 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>将案例分别转换为案例 1 或案例 2。

我希望你现在已经了解了所有的差异,所以我不会重复这里发生的细节。


免责声明:所有示例都只是说明性的。这些代码都没有经过测试,这些代码都不是“完整的”。例如,缺少很多错误检查。它们只是为了展示机制的骨架。由于拼写错误等,它们甚至可能无法编译。

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

最后一句话#2:请注意,还有参考资料如果模块 B/C通过引用捕获了Base*or ,那么引入or就没有意义了。引用捕获已经引入了所需的相同的多一级间接。事实上,引用 & 在内部只是一个原始指针,事实上也是如此。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
于 2013-05-15T07:47:25.660 回答