Reputation: 305
I have a base class, BaseObject
, and two derived class DerivedObject1
and DerivedObject2
. They share a common behavior and methods, but DerivedObject1
has an additional method. My main class MyClass
stores (in std::vector
) boost::shared_ptr
of instances of those classes. MyClass
needs to call commonMethod()
for all the BaseObject, and sometimes call additionalMethod()
for all DerivedObject1.
class BaseObject
{
virtual void commonMethod();
}
Class DerivedObject1 : public BaseObject
{
void commonMethod();
void additionalMethod();
}
Class DerivedObject2 : public BaseObject
{
void commonMethod();
}
Are there any disadvantages of having two vectors in MyClass
, one that stores ALL the pointers of DerivedObject1
and DerivedObject2
, and another vector that stores only the pointers of DerivedObject1
? Meaning I would have all the DerivedObject1
pointers twice. But I think the call to the different methods would be clear, at least.
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();
}
}
}
I can think of other ways to do this, mainly having one vector for DerivedObject1
and one vector for DerivedObject2
, but to call commonMethod()
, I would have to iterate over both vectors. My original solution seems the best to me, except that some pointers are stored twice. What are the disadvantages of this ?
Upvotes: 7
Views: 427
Reputation: 2880
Interesting question. We sometimes meet such a situation in maintenance of legacy codes which we don’t know who wrote.
Are there any disadvantages of having two vectors in MyClass … ?
I think there are no mechanical (or performance) disadvantages. If we are struggled to meet the release deadline, we have no choice but to pick such an simple way. However, storing same vectors twice actually lowers maintainability and we should consider improving it in the future.
If you need dynamic_pointer_cast
to implement functions like @Caleth ’s push_back
/ remove
,
how about removing only_derived1
and reluctantly applying just one dynamic_pointer_cast
in doSomethingForDerivedObject1()
from the get-go as follows ?
It will make MyClass simpler. Required modification will not be complicated if DerivedObject3 is defined in the future.
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();
}
}
}
Declaring virtual function BaseObject::additionalMethod()
and implementing
void DerivedObject2::additionalMethod()
{
/* nothing to do */
}
then you can again remove only_derived1
.
In this method you must implement DerivedObject3::additionalMethod()
only if DerivedObject3 is defined.
But, although it depends on your constructor or setter code, if the following case will also occur
everything; ->derived2
only_derived1; ->derived1
this method is still insufficient.
Ideally, we should not use public inheritance to implement objects within "IS-ALMOST-A“ relations, as Herb Sutter says.
The relation between BaseObject
, DerivedObject1
and DerivedObject2
looks like this one.
Since I don’t know whole code of your application I may be wrong, but it will be worthwhile to consider extracting DerivedObject1::additionalMethod()
as another class or a function pointer and putting its vector in MyClass as a private member.
Upvotes: 6
Reputation: 62719
Yes, your first method is good, the main problem is that you end up duplicating the public members of vector
to ensure that the consistency of the Derived
vector.
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...
};
What you can instead do is provide a view of your bases
using something like boost::range
's adaptors
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));
Upvotes: 0
Reputation: 40481
I can suggest this : Store everything in one array , and make a dummy additionalMethod()
in DerivedObject2
. Then - just invoke additionalMethod
for every object .
Alternativly :
They share a common behavior and methods, but DerivedObject1 has an additional method
Make DerivedObject1
inherit from DerivedObject2
Upvotes: 1