I wondered if there is a common way/pattern to let a derived class have a somewhat more specialized version of a pure virtual method in the given base class.
class Base {
public:
Base() = default;
virtual ~Base() = 0;
virtual void foo(int bar) = 0;
};
inline Base::~Base() {}
class Derived public Base {
public:
Derived() = default;
~Derived() = default;
void foo(int bar) override {/*...*/}
};
class SpecializedDerived : public Base {
public:
SpecializedDerived() = default;
~SpecializedDerived() = default;
void foo(int bar, double additionalParameter) override {/*...*/}
};
The override in the SpecializedDerived
class is impossible, because the signature of the method does not correspond to the one in the pure virtual Base
class.
Now, is there a way to achieve the described design? Is there a way to implement "more specialized methods" as there is class inheritance which will allow you to implement "more specialized classes"?
While typing I reckoned that my wish is more a kind of "Dude, I just want you to provide an iterate(.)
function of some sort!" thing.
The only idea that came to my mind so far is something like
class Base {
public:
Base() = default;
virtual ~Base() = 0;
virtual void foo(int bar) = 0;
};
inline Base::~Base() {}
class SpecializedDerived : public Base {
public:
SpecializedDerived(double addParam) : additionalParam_(addParam) {}
~SpecializedDerived() = default;
void foo(int bar) override {
iterate(bar, additionalParam_);
return;
}
private:
double additionalParam_;
void foo(int bar, double additionalParam) {/*...*/}
};
Where this internal function call is actually redundant as you could just do:
class SpecializedDerived : public Base {
public:
SpecializedDerived(double addParam) : additionalParam_(addParam) {}
~SpecializedDerived() = default;
void foo(int bar) override {/* code using additionalPara_ */}
private:
double additionalParam_;
};
The idea behind polymorphism and virtual functions, is that the caller doesn't have to know any details about the real class of the object it uses:
Example:
Base *my_object = find_dynamically_a_relevant_object (...);
my_object->foo(10); // could be a Derived or a SpecializedDerived
std::vector<Base*> container;
... // somehow you populate the container with a mix of
... // Derived AND SpecializedDerived objects
for (auto x: container)
x->foo(std::srand());
This is why the signature must match exactly the one defined in the base class.
Now you could very well define an overloaded foo()
with a completely different signature under three conditions:
override
: it's a different function with the same name. * You could invoke the overloaded foo() with its additional parameters only if you're sure that the object has the right type.Example:
class SpecializedDerived : public Base {
public:
SpecializedDerived() = default;
~SpecializedDerived() = default;
void foo(int bar) override { foo(bar, 0.0); }
void foo(int bar, double additionalParameter) {cout<<"specialized "<<bar<<" "<<additionalParameter<<endl;}
};
... // elsewhere, for example in main():
SpecializedDerived my_obj;
my_obj.foo(10); // use the override of the base
my_obj.foo(10, 37.2); // use the overload
// suppose that p is a Base* like in the first example
auto *q = dynamic_cast<SpecializedDerived*>(p);
if (q) // dynamic cast makes this nullptr if not the right type
q->foo(10, 37.2);
else cout << "Not specialized"<<endl;
Now if you want to use your foo() in strictly polymorphic context but still have some (hidden) additional parameters, there are several possibilities, such as for example:
foo()