我有一个基类BaseObject
,还有两个派生类DerivedObject1
和DerivedObject2
。它们具有相同的行为和方法,但是DerivedObject1
具有其他方法。我的主类MyClass
在std::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
中有两个向量有一个缺点,一个向量存储DerivedObject1
和DerivedObject2
的所有指针,另一个向量仅存储{{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
,我必须遍历这两个向量。我的原始解决方案对我来说似乎是最好的,除了一些指针存储两次。这有什么缺点?
答案 0 :(得分:6)
有趣的问题。 在维护遗留代码时,有时我们会遇到这种情况,我们不知道是谁写的。
在MyClass中有两个向量是否有任何缺点……?
我认为没有机械(或性能)上的缺点。 如果我们很难在发布期限之前完成任务,那么我们别无选择,只能选择这种简单的方法。 但是,将相同的向量存储两次实际上会降低可维护性,我们应该在将来考虑对其进行改进。
如果您需要dynamic_pointer_cast
来实现@Caleth的push_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”关系中的对象。
BaseObject
,DerivedObject1
和DerivedObject2
之间的关系如下所示。
由于我不知道您应用程序的全部代码,所以我可能是错的,但是值得考虑提取DerivedObject1::additionalMethod()
作为另一个类或函数指针,并将其向量作为私有成员放入MyClass中。
答案 1 :(得分:1)
我可以建议:将所有内容存储在一个数组中,并在additionalMethod()
中创建一个虚拟DerivedObject2
。然后-只需为每个对象调用additionalMethod
。
替代:
它们具有相同的行为和方法,但是DerivedObject1具有其他方法
使DerivedObject1
从DerivedObject2
继承
答案 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));