Background:
See this question in the C++ FAQ for a similar situation that I need to solve, but with named constructors.
I have a Base class, class B
.
I have a Derived class from B, class D
, that adds additional functionality via functions, members, and additional memory allocation.
The additional functionality is polymorphically supported in class B
by doing nothing or returning default values and nullptrs
from the virtual functions specific to class D
.
class B
uses public static Factory Methods
for construction with all protected constructors
. (see: Named Constructor Idiom)
class D
uses public static Factory Methods
for construction with all protected constructors
that are named differently from class B and not available in class B.
Sometime later, a new interface class is created, class A
. This class has an interface such that the derived classes from class A
must have a getter function and a setter function that both require a pointer to a class B
but the dynamic value can be either class B
or class D
Question:
I want to derive class A
and create a copy constructor, an assignment operator, and/or a setter for class B
but because class A
only exposes its member(s) as an object of type B
I have no way of determining if the object returned is class B
or class D
.
How would I correctly implement the above using only the public interface without causing slicing or memory issues (including if the above is set up wrong and needs to be changed)?
Possible solutions?:
I'm tempted to try a couple of options:
1) Create a member in class B and all derived types that declares the type of object:
if(getB()->GetType() == "D") {
//Call D::CreateD(...)
} else if(getB()->GetType() == "B") {
//Call B::CreateB(...)
}
2) Dynamically cast to the derived type and check for failure:
if(dynamic_cast<D*>(getB()) == nullptr) {
//Call B::CreateB(...)
} else {
//Call D::CreateD(...)
}
3) Use a virtual method that is specific to class D
that I know returns nullptr
when used on a class B
object:
if(getB()->VirtualMethodSpecificToClassD() == nullptr) {
//Call B::CreateB(...)
} else {
//Call D::CreateD(...)
}
All three cases have code smells:
As per zneak
's comment, I think that if you're using factory methods and private constructors, there's nothing too smelly about having a
virtual B* copy() const { return new B(*this); /* calls private B::B(const B&) */ }
method in class B
, overridden in class D
(returning a new D*
-- this usage of covariant return types is specifically permitted in C++).
Then your A
copy constructor can do something like
A::A(const A& other) : b(other.getB()->copy()) {}
and it should work okay.
On the other hand, if you'd rather go with one of the solutions you've suggested, I think that the first is the least pungent -- although I'd go for an enum rather than a string, so you can use a simple switch statement rather than string compares. I believe LLVM uses something like this for "dynamic casting", to avoid the C++ RTTI overhead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With