J'ai une classe de base, BaseObject
, et deux classes dérivées DerivedObject1
et DerivedObject2
. Ils partagent un comportement et des méthodes communs, mais DerivedObject1
a une méthode supplémentaire. Ma classe principale MyClass
stocke (dans std::vector
) boost::shared_ptr
des instances de ces classes. MyClass
doit appeler commonMethod()
pour tout le BaseObject, et parfois aussi additionalMethod()
pour tout DerivedObject1.
class BaseObject
{
virtual void commonMethod();
}
Class DerivedObject1 : public BaseObject
{
void commonMethod();
void additionalMethod();
}
Class DerivedObject2 : public BaseObject
{
void commonMethod();
}
Existe-t-il des inconvénients à avoir deux vecteurs dans MyClass
, un qui stocke TOUS les pointeurs de DerivedObject1
et DerivedObject2
et un autre vecteur ne stockant que les pointeurs de DerivedObject1
? Ce qui signifie que j'aurais tous les pointeurs DerivedObject1
deux fois. Mais je pense que l'appel aux différentes méthodes serait clair, au moins.
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();
}
}
}
Je peux penser à d’autres moyens de le faire, principalement en ayant un vecteur pour DerivedObject1
et un vecteur pour DerivedObject2
, mais pour appeler commonMethod()
, il me faudrait parcourir les deux vecteurs. Ma solution initiale me semble la meilleure, sauf que certains pointeurs sont stockés deux fois. Quels sont les inconvénients de ceci?
Question intéressante. Nous sommes parfois confrontés à une telle situation en ce qui concerne le maintien des codes hérités, dont nous ne savons pas qui a écrit.
Y at-il des inconvénients à avoir deux vecteurs dans MyClass…?
Je pense qu'il n'y a pas d'inconvénients mécaniques (ou de performances). Si nous avons du mal à respecter le délai de publication, nous n'avons pas d'autre choix que de choisir un moyen aussi simple. Cependant, stocker deux fois les mêmes vecteurs réduit réellement la maintenabilité et nous devrions envisager de l'améliorer à l'avenir.
Si vous avez besoin de dynamic_pointer_cast
pour implémenter des fonctions telles que @Caleth 's Push_back
/remove
, Que diriez-vous de supprimer only_derived1
et d'appliquer à contrecœur un seul dynamic_pointer_cast
dans doSomethingForDerivedObject1()
de la manière suivante? .] Cela simplifiera MyClass. La modification requise ne sera pas compliquée si DerivedObject3 est défini à l'avenir.
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();
}
}
}
Déclaration de la fonction virtuelle BaseObject::additionalMethod()
et implémentation
void DerivedObject2::additionalMethod()
{
/* nothing to do */
}
vous pouvez alors à nouveau supprimer only_derived1
. Dans cette méthode, vous devez implémenter DerivedObject3::additionalMethod()
uniquement si DerivedObject3 est défini.
Mais, bien que cela dépende de votre code constructeur ou setter, si le cas suivant se produit également
everything; ->derived2
only_derived1; ->derived1
cette méthode est encore insuffisante.
Idéalement, nous ne devrions pas utiliser l'héritage public pour implémenter des objets dans les relations "IS-ALMOST-A", comme le dit Herb Sutter. La relation entre BaseObject
, DerivedObject1
et DerivedObject2
ressemble à celle-ci. Depuis Je ne connais peut-être pas le code complet de votre application, mais il serait utile d'envisager d'extraire DerivedObject1::additionalMethod()
en tant que classe ou de pointeur de fonction et de placer son vecteur dans MyClass en tant que membre privé.
Je peux suggérer ceci: stockez tout dans un tableau et créez une additionalMethod()
factice dans DerivedObject2
. Ensuite, appelez simplement additionalMethod
pour chaque objet.
Alternativement:
Ils partagent un comportement et des méthodes communs, mais DerivedObject1 a une méthode supplémentaire
Faire DerivedObject1
hériter de DerivedObject2
Oui, votre première méthode est bonne, le problème principal est que vous finissez par dupliquer les membres publics de vector
pour vous assurer de la cohérence du vecteur 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...
};
Ce que vous pouvez faire à la place est de fournir une vue de votre bases
en utilisant quelque chose comme: les adaptateurs boost::range
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));