The constructor of class StructComponent takes a different logic to initialize its member variables based on the type of the pass-in object of info. Here I use casting to convert the pass-in parameter to right subclass object.
class StructComponent
{
public:
StructComponent(const ClassA& info)
{
if (info.getType() == CLASS_B)
{
const ClassC& classC = dynamic_cast<const ClassC&> info;
...
apply a different logic for ClassB and init member accordingly
} else if (info.getType() == CLASS_C) {
apply a different logic for others
...
} else {
apply default
}
}
}
class ClassA
{
public:
ClassA(...)
{
m_shp = CreateStructComponent();
}
virtual boost::shared_ptr<StructComponent> CreateStructComponent()
{
return boost::shared_ptr<StructComponent> (new StructComponent(*this));
}
...
int getType() const { return CLASS_A; }
protected:
boost::shared_ptr<StructComponent> m_shp;
}
class ClassB : public ClassA
{
public:
...
virtual boost::shared_ptr<StructComponent> CreateStructComponent()
{
return boost::shared_ptr<StructComponent> (new StructComponent(*this));
}
...
int getType() const { return CLASS_B; }
}
class ClassC : public ClassA
{
public:
...
virtual boost::shared_ptr<StructComponent> CreateStructComponent()
{
return boost::shared_ptr<StructComponent> (new StructComponent(*this));
}
...
int getType() const { return CLASS_C; }
}
Q1> Is the code correct ignore the potential design issue?
Q2> Assuem all subclass of ClassA has the same implementation body of the function CreateStructComponent.
Is there a way that I can save space not to repeatedly do the same code as follows:
return boost::shared_ptr<StructComponent> (new StructComponent(*this));
Q3> Is there a better design that I can use? For example, is there a way that I can ignore casting in the
StructComponent?
1) No, it’s not correct, at least, it doesn’t do what you probably expect. It calls a virtual function in the constructor of
ClassA, which will always callClassA::CreateStructComponent()instead of calling the overriding function in the derived class, because when theClassAconstructor runs that dynamic type isClassA. For the same reason, in the constructor forStructComponentthegetType()call will always resolve toClassA::getType().2) There are lots of ways to solve that problem. You could put the code in a template, so it depends on the type, or you could just get rid of the need to duplicate the code, by doing the initialization in a different place.
3) Why not simply give
StructComponentoverloaded constructors, one takingClassB, one takingClassCand another takingClassA?Then again, a better solution would probably be to get rid of the
StructComponentconstructor and haveClassA,ClassB, orClassCdo the initialization explicitly, so that each type initializes it how it wants it done. If the initialization depends on the type that creates it, the initialization doesn’t belong in theStructComponentconstructor. Currently you have a circular dependency,StructComponentneeds to know about all the types that use it, and all the types that use it need to know aboutStructComponent. That’s usually a sign of a problem with the design, all the classes are tightly coupled to each other. It would be better ifStrictComponentknew nothing about the other types.Anyway, here’s one possible solution, showing a way to pass the correct type to the
StructComponentwithout duplicating code, but I don’t think this is a good design. Note that theStructcomponentconstructor is passed NULL, so it can do different things based on the type, but cannot access the objects it is passed.Here’s another, completely-different approach without virtual base hacks, still removing duplicated code, and allowing
StructComponentto access the objects passed to it (which I maintain is a bad idea):And here’s yet another choice, this time without the circular dependencies, moving the different initialization code where it belongs: