I'm writing a project in which I'm using strategy, however, my derived classes have other additional functionalities which the base class shouldn't have.
class Base {
public:
virtual void execute() = 0;
virtual void print() const noexcept = 0;
virtual ~Base() {}
};
class DerivedA : public virtual Base {
public:
void execute() override;
void print() const noexcept override;
void doSomething();
private:
int x;
double y;
};
class DerivedB final : public Base {
public:
void execute() override;
void print() const noexcept override;
std::string getZ() const noexcept;
private:
std::string z;
};
In main(), I'm trying to use dynamic_cast to be able to use this additional functionality like this:
int main() {
DerivedA da;
Base* base = &da;
DerivedA* derivedA = dynamic_cast<DerivedA*>(base);
derivedA.doSomething();
return 0;
}
But when I try to run the code I get the following error:
'DerivedA *' differs in levels of indirection from 'DerivedA'
My questions are, should I be even using strategy for this, or should I be using another design pattern? And if I should use strategy, how can I solve this error?
UPDATE
I'm thinking of switching to decorator, since the DerivedA can be a base class for DerivedB, however, I'm worried about casting, since each class has different class members.
UPDATE
Seems like I wrote the casting wrong. However, I wrote it right when posting it here! It works now, but, I agree that there's a better way to implement this.
If this is exactly the code you're trying to compile, the solution is easy. Instead of
derivedA.doSomething();
you should write
derivedA->doSomething();
Since derived is a pointer, not a reference or instance.
However, I do have some other serious concerns about considering dynamic_cast
. My opinion is that if you use dynamic_cast
there is probably something wrong in your design. You have an interface hiding the implementations, yet have code that relies on the implementations and not in the interface.
Some alternatives to consider:
IAnimal
, and some of your animals can swim, make an interface ISwimmable
and add a method to IAnimal
that return a pointer to ISwimmable
( IAnimal::getSwimmable
). Animal implementations that can swim, can inherit from ISwimmable
and implement getSwimmable
and return a pointer to the ISwimmable
interface (so actually, return an upcast of itself, which is simply implicitly itself). Animals that can't swim can return a nullptr
(which could be the default implementation in IAnimal
or some AnimalBase
class inheriting from IAnimal
if you want to keep the interface a pure interface). This second approach is also used by Microsoft's COM system, where every COM interface implements the IUnknown
interface, and where you can call IUnknown
to get a specific, different interface for some 'capability'.
Code below iw working file for me, with g++ and compilation: g++ main.cc -o main -std=c++14
#include <string>
class Base {
public:
virtual void execute() = 0;
virtual void print() const noexcept = 0;
virtual ~Base() {}
};
class DerivedA : public virtual Base {
public:
void execute() override {}
void print() const noexcept override {}
void doSomething(){}
private:
int x;
double y;
};
class DerivedB final : public Base {
public:
void execute() override {}
void print() const noexcept override {}
std::string getZ() const noexcept {}
private:
std::string z;
};
int main() {
DerivedA da;
Base* base = &da;
DerivedA* derivedA = dynamic_cast<DerivedA*>(base);
derivedA->doSomething();
return 0;
}
What you're describing is a common problem in software, and one that crops up a lot in various design patterns. You want to use DerivedA
and DerivedB
polymporphically, so they have to share a common interface. That's fine. However DerivedA
has a useful method doSomething
you want to use as well. Your options are either:
doSomething
method, which keeps your code nice and clean; doSomething
, but then your code unavoidably looks a bit hacky; doSomething
in Base
to do nothing, then your code looks nice, but when you call doSomething
on a Base
reference, it will do nothing if the receiving object in actually of class DerivedB
. Decorator won't really help here. Decorator is good for augmenting existing methods. You can't use it (in a statically typed language) to add new methods, as I learned the hard way :-)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.