I am trying to reduce code duplication in an init function that initializes static members of derived classes. In my case, this function is called initTypeInfo. Originally, each of my child classes ChildA, ChildB ... ChildN had their own initTypeInfo. Once I decided to move initTypeInfo into the Parent class because pretty much all the code except the namespace of the static member (ChildA::s_typeInfo vs. ChildB::s_typeInfo ...) was the same, I started to see problems with the static member s_typeInfo. This variable is a smart pointer of type std::shared_ptr<TypeInfo>
Here is my code sample:
#include <string>
#include <memory>
#include <iostream>
struct TypeInfo
{
std::string name;
// other members ...
};
class Parent
{
public:
Parent(std::string name)
: m_name(name)
{
}
virtual std::shared_ptr<TypeInfo> getTypeInfo() = 0;
virtual void initTypeInfo()
{
// this function contains lots of common code, trying to avoid code duplication
std::shared_ptr<TypeInfo> typeInfo = getTypeInfo();
// initialize if it hasn't already been initialized
if (typeInfo == nullptr)
{
TypeInfo info {m_name};
// the variable Child<n>::s_typeInfo doesn't point to this allocated data :(
typeInfo = std::make_shared<TypeInfo>(info);
}
}
private:
std::string m_name;
};
class ChildA : public Parent
{
public:
ChildA(std::string name /*other params...*/)
: Parent(name)
{
initTypeInfo();
}
std::shared_ptr<TypeInfo> getTypeInfo()
{
return s_typeInfo;
}
private:
inline static std::shared_ptr<TypeInfo> s_typeInfo = nullptr;
// other members...
};
class ChildB : public Parent
{
public:
ChildB(std::string name /*other params...*/)
: Parent(name)
{
initTypeInfo();
}
std::shared_ptr<TypeInfo> getTypeInfo()
{
return s_typeInfo;
}
private:
inline static std::shared_ptr<TypeInfo> s_typeInfo = nullptr;
// other members...
};
int main()
{
ChildA ca1("childa1");
ChildA ca2("childa2");
ChildB cb1("Childb1");
return 1;
}
So my questions are:
- Is this bad design to begin with, where I initialize the static member in the parent class
- How can the code duplication here be addressed?
- I know returning by value is the preferred way to return smart pointers from a function. But in this case, how can I have my member
Child<n>::s_typeInfopoint to the data I allocated inParent::initTypeInfo()
The links I followed online pointed me to this link so I suppose I could achieve this with templates, but I'm not convinced that's the only way to do this.
Edit1:
In practice TypeInfo doesn't only contain one data member. It's a complex class with several data members and methods. Initially I had initTypeInfo functions in each derived class which would initialize each s_typeInfo. However, turns out that the logic in all these initTypeInfo functions were the same! The only difference was that the specific s_typeInfo of each derived class had to be initialized. So I wanted a way to transfer the logic into the Parent class (thereby reducing duplication) but also init the static s_typeNode in the parent. I guess, alternatively the question could be, how do I get the child class shared_ptr to point to the TypeInfo I create in Parent::initTypeInfo()?
The reason s_typeInfo exists as a static member is because I want to share that class across all instances of a certain Child. So in my main if for example I start 4 instance of ChildA I need them to share a single ChildA::s_typeInfo and if I have 10 instances of ChildB, I need them to share a single ChildB::s_typeInfo.
Edit2:
Adding minimal example of code for what I want to achieve. I was expecting *(c.p) to be 1 instead of nullptr as I had returned from C::get() by reference.
class C
{
public:
std::shared_ptr<int> & get()
{
return p;
}
inline static std::shared_ptr<int> p = nullptr;
};
int main()
{
C c;
std::shared_ptr<int> p1 = c.get();
if (p1 == nullptr)
{
p1 = std::make_shared<int>(1);
}
std::cout << *p1 << std::endl;
if (c.p == nullptr)
{
std::cout << "nullptr" << std::endl;
}
else
{
std::cout << *(c.p) << std::endl;
}
return 0;
}