如何在C ++中实现此数据结构

时间:2019-03-01 21:31:51

标签: c++ pointers smart-pointers

我正在从C#/ Java迁移到C ++,并且难以理解正确的处理方式。

我正在建立一种数据结构来支持算法和功能。因此,假设一个外部对象包含一个std::vector<Widget> vecW和一个std::vector<Vidget> vecV。它创建许多类型为Widget的对象,并将它们添加到vecW中。随后,它将创建多个Vidget的实例,并将它们添加到vecV中。 (以防万一:在外部对象被破坏时,应该释放为这些对象分配的内存。)

现在,对于列表中每个Widget类型的对象,有一些自定义逻辑指示它需要访问某些Vidget类型的对象。 (反之亦然,如果窗口小部件w可以访问vidget v,则vidget v需要访问窗口小部件w。)

在C#中,我只需要在每个List<vidget> ListOfVidgets中保留一个Widget,并在每个List<Widget> ListOfWidgets中保留一个Vidget,然后根据自定义逻辑实例化这些列表。与Java类似(我相信C#中的List<Vidget>类似于Java中的arraylist<Vidget>,有点像C ++中的std::vector<Vidget*> / std::vector<Vidget>)。

因此,我可以在每个小部件中使用std::vector<Vidget*>,以根据自定义逻辑进行实例化。这是最好的方法吗?还是有首选的带有智能指针的方法(甚至其他方法)?


编辑:生命周期如下:1)创建并填充外部对象get(带有小部件/ vidgets)。 2)定制逻辑确定关系。 3)使用数据结构。 (因此,在使用过程中不会更改关系和/或添加/删除小部件。)4)(仅)当外部对象被破坏时,则需要释放内存。

4 个答案:

答案 0 :(得分:2)

这就是我要做的。

class Widget {
  ...
    void AddVidget(Vidget* vidget);
  private:
    std::vector<Vidget*> vigets_;
};
std::vector<std::unique_ptr<Vidget>> vidgets;
// Since widgets will have references to vidgets, safet
// to instantiate after vidgets (so widgets is cleaned up
// first).
std::vector<std::unique_ptr<Widget>> widgets;
...
widgets[i].add_vidget(vidjets[j].get());

这为您提供了指针稳定性。

答案 1 :(得分:1)

使用std::vector的主要问题在于,当向量改变时,尤其是在填充向量时,所包含对象的地址可能会改变。

鉴于您的生命周期特征,将在之前完全创建对象,并且在之后处理结束之前不会更改任何关系。这意味着您无需为关系使用指针。

在这种情况下,我可能会做这样的事情:

class Widget
{
public:

private:
    std::vector<class Vidget*> ListOfVidgets;
};

class Vidget
{
public:

private:    
    std::vector<class Widget*> ListOfWidgets;
};

class OuterObject
{
public:

private:
    std::vector<Widget> vecW;
    std::vector<Vidget> vecV;
};

随着所有权语义的确立,这应该非常有效。外部向量拥有一切,内部对象向量仅包含非所有者关系指针。

答案 2 :(得分:0)

通常,我首先会寻找一种重新设计的方法,以使这一要求消失。这样,对象通常不必通过其上方的对象来查找其他对象。有时,您可以安排每次对象所有者执行对象时传递对象信息。

但是假设您无法更改设计,则您既不想使用指针也不能使用引用。您不希望使用它们,因为向量可以在内存中移动它们的对象,从而使指针和引用无效。另外,普通指针会使所有权不清楚。

相反,对对象使用std::shared_ptr s向量。如果可能,使用std::make_shared创建对象。这样可以管理对象的生存期,直到将其从向量中删除为止。

对于需要访问另一个对象的一个​​对象,您有两种选择。如果您想延长被引用对象的生存期,直到拥有引用的对象被销毁为止,请对同一对象使用另一个std::shared_ptr

如果您不想延长对象的生存期,请使用std::weak_ptr -请记住,如果目标的生存期结束,则弱指针可能会过时。您要么需要确保这种情况永远不会发生,要么每次访问它时都要检查弱指针。最好总是总检查一下,以理智地处理错误情况。

按照我的个人喜好,您可以:

  1. 重新架构,因此无需存储这些引用。例如,外部对象可以拥有这些关系,并在每次调用内部对象时将引用传递给内部对象。甚至还有更好的选择。外部对象可以提供内部对象可以使用的API,并且内部对象可以保留对外部对象的引用。

  2. 使用shared_ptrweak_ptr。这是干净的,易于理解的并且可以干净地捕获任何错误。它有一些开销,但仅涉及设置和拆卸,如果这些对象是长期的,则无关紧要。

  3. 使用自定义智能指针。外部对象可以为内部对象提供自定义智能指针,该指针为内部对象提供了一种访问所需对象的简便方法。

  4. 使用unique_ptr和原始指针的向量。

答案 3 :(得分:-2)

不要弄乱指针。那是旧的C风格的思维方式。

如果要使用C ++处理对象,则制作WidgetVidget的列表/向量/映射,并让STL容器拥有所讨论的对象。然后,您不必担心会取消引用错误的指针等。

关于下一个要求,Widgets需要访问Vidgets,反之亦然,您需要在映射中实现该逻辑。我认为是从数据库的角度出发,所以我有一张表格,该表格将W到V和V到W映射,并带有将引用返回必要对象的方法。对于C ++,我可能会使用带有某种ID(如果需要的话是人造的)的std :: map到小部件或vidget来实现。