将相同共享指针的副本存储在不同的向量中是一种好习惯吗?

Is it good practice to store copies of the same shared pointers in different vectors?

我有一个基数 class、BaseObject 和两个派生的 class DerivedObject1DerivedObject2。它们具有共同的行为和方法,但 DerivedObject1 有一个额外的方法。我的主要 class MyClass 商店(在 std::vector 中)boost::shared_ptr 那些 classes 的实例。 MyClass需要对所有BaseObject调用commonMethod(),有时对所有DerivedObject1调用additionalMethod()

class BaseObject
{
  virtual void commonMethod();
}

Class DerivedObject1 : public BaseObject
{
  void commonMethod();
  void additionalMethod();
}

Class DerivedObject2 : public BaseObject
{
  void commonMethod();
}

MyClass 中有两个向量有什么缺点,一个存储 DerivedObject1DerivedObject2 的所有指针,另一个向量只存储 [=] 的指针13=] ?这意味着我将所有 DerivedObject1 指针两次。但我认为至少对不同方法的调用会很清楚。

class MyClass
{
  typedef std::vector<std::shared_ptr<BaseObject>> BaseObjectVector;
  typedef std::vector<std::shared_ptr<DerivedObject1>> DerivedObject1Vector;
  BaseObjectVector everything;
  DerivedObject1Vector only_derived1;

  void doSomething()
  {
    for (BaseObjectVector::iterator iter = everything.begin(); iter != everything.end(); ++iter)
    {
      (*iter)->commonMethod();
    }
  }

  void doSomethingForDerivedObject1()
  {
    for (DerivedObject1Vector::iterator iter = only_derived1.begin(); iter != only_derived1.end(); ++iter)
    {
      (*iter)->additionalMethod();
    }
  }
}

我可以想到其他方法来做到这一点,主要是 DerivedObject1 有一个向量,DerivedObject2 有一个向量,但是要调用 commonMethod(),我将不得不迭代两个向量。我原来的解决方案对我来说似乎是最好的,除了一些指针被存储了两次。这有什么缺点?

我可以这样建议:​​将所有内容存储在一个数组中,然后在 DerivedObject2 中创建一个虚拟 additionalMethod()。然后 - 只需为每个对象调用 additionalMethod .

或者:

They share a common behavior and methods, but DerivedObject1 has an additional method

使 DerivedObject1 继承自 DerivedObject2

是的,你的第一种方法很好,主要问题是你最终复制了vector的public成员,以确保Derived向量的一致性。

class MyClass
{
    typedef std::vector<std::shared_ptr<BaseObject>> BaseObjectVector;
    typedef std::vector<std::shared_ptr<DerivedObject1>> DerivedObject1Vector;
    BaseObjectVector everything;
    DerivedObject1Vector only_derived1;
public:
    void push_back(shared_ptr<Base> ptr)
    {
        everything.push_back(ptr);
        if (shared_ptr<Derived1> derived = dynamic_ptr_cast<Derived1>(ptr))
        {
            only_derived1.push_back(derived);
        }
    }
    void remove(shared_ptr<Base> ptr)
    {
        base.remove(ptr);
        only_derived1.remove(dynamic_ptr_cast<Derived1>(ptr));
    }
    // dozens more... 
};

你可以做的是使用 boost::range's adaptors

之类的东西提供你的 bases 的视图
shared_ptr<Derived1> convert(shared_ptr<Base> ptr)
{
    return dynamic_ptr_cast<Derived1>(ptr);
}

bool not_null(shared_ptr<Derived1> ptr)
{
    return ptr.get();
}

boost::for_each(bases 
              | boost::adaptors::transformed(convert) // Base to Derived
              | boost::adaptors::filtered(not_null)   // Eliminate null
              | boost::adaptors::indirected,          // dereference
                boost::bind(&Derived1::additionalMethod, boost::placeholders::_1));

有趣的问题。 我们在维护不知道是谁写的遗留代码时,有时会遇到这样的情况。

Are there any disadvantages of having two vectors in MyClass … ?

我认为没有机械(或性能)缺点。 如果我们在发布截止日期前苦苦挣扎,我们别无选择,只能选择这种简单的方式。 但是,存储相同的向量两次实际上会降低可维护性,我们应该考虑在未来改进它。


  • std::dynamic_pointer_cast(或boost::dynamic_pointer_cast)[Demo]

如果你需要dynamic_pointer_cast来实现像push_back/remove这样的功能, 从一开始就删除 only_derived1 并不情愿地只在 doSomethingForDerivedObject1() 中应用一个 dynamic_pointer_cast 怎么样? 它将使 MyClass 更简单。如果以后定义了DerivedObject3,所需的修改就不会复杂了。

void MyClass::doSomethingForDerivedObject1()
{
    for (const auto& obj_i : everything)
    {
      if (auto derived1 = std::dynamic_pointer_cast<DerivedObject1>(obj_i))
        {
           derived1->additionalMethod();
        }
    }
}

void MyClass::doSomethingForDerivedObject3()
{
    for (const auto& obj_i : everything)
    {
      if (auto derived3 = std::dynamic_pointer_cast<DerivedObject3>(obj_i))
        {
           derived3->additionalMethod();
        }
    }
}

  • [Demo]
  • 提出的虚拟方法

声明虚函数BaseObject::additionalMethod()并实现

void DerivedObject2::additionalMethod()
{ 
  /* nothing to do */
}

然后您可以再次删除 only_derived1。 在此方法中,仅当定义了 DerivedObject3 时才必须实施 DerivedObject3::additionalMethod()

但是,虽然这取决于你的构造函数或者setter代码,如果也会出现下面的情况

everything;    ->derived2
only_derived1; ->derived1

这个方法还是不够的。


理想情况下,我们不应该使用 public 继承来实现“IS-ALMOST-A”关系中的对象,如 Herb Sutter 所说。 BaseObjectDerivedObject1DerivedObject2 之间的关系如下所示。 因为我不知道你应用程序的整个代码,所以我可能是错的,但是考虑将 DerivedObject1::additionalMethod() 提取为另一个 class 或函数指针并将其向量作为私有成员放入 MyClass 中是值得的.