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

时间:2018-10-03 12:43:51

标签: c++ shared-ptr c++03

我有一个基类BaseObject,还有两个派生类DerivedObject1DerivedObject2。它们具有相同的行为和方法,但是DerivedObject1具有其他方法。我的主类MyClassstd::vector中存储boost::shared_ptr这些类的实例。 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的所有指针,另一个向量仅存储{{1}的指针}?意思是我将所有DerivedObject1指针两次。但我认为,至少可以清楚地知道使用不同方法的情况。

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,我必须遍历这两个向量。我的原始解决方案对我来说似乎是最好的,除了一些指针存储两次。这有什么缺点?

3 个答案:

答案 0 :(得分:6)

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

  

在MyClass中有两个向量是否有任何缺点……?

我认为没有机械(或性能)上的缺点。 如果我们很难在发布期限之前完成任务,那么我们别无选择,只能选择这种简单的方法。 但是,将相同的向量存储两次实际上会降低可维护性,我们应该在将来考虑对其进行改进。


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

如果您需要dynamic_pointer_cast来实现@Calethpush_back / remove之类的功能,  如何从一开始就删除only_derived1并勉强地在dynamic_pointer_cast中应用一个doSomethingForDerivedObject1(),如下所示? 这将使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();
        }
    }
}

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

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

然后您可以再次删除only_derived1。 在此方法中,只有在定义了DerivedObject3的情况下,才必须实现DerivedObject3::additionalMethod()

但是,尽管这取决于您的构造函数或设置器代码,但是否还会发生以下情况

everything;    ->derived2
only_derived1; ->derived1

这种方法仍然不够。


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

答案 1 :(得分:1)

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

替代:

  

它们具有相同的行为和方法,但是DerivedObject1具有其他方法

使DerivedObject1DerivedObject2继承

答案 2 :(得分:0)

是的,您的第一种方法很好,主要问题是最终要复制vector的公共成员以确保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));