I try to understand how double dispatch works. I created an example where a monster and a warrior derived from the abstract class Creature could fight. The class Creature has method "fight", which is defined in derived classes, and in each derived class is defined what happens if warrior fights with warrior or with monster etc. I wrote the following code:
#include<iostream>
using namespace std;
class Monster;
class Warrior;
class Creature{
public:
virtual void fight(Creature&) =0;
};
class Monster: public Creature{
void fightwho(Warrior& w) {cout<<"Monster versus Warrior"<<endl; }
void fightwho(Monster& m) {cout<<"Monster versus Monster"<<endl; }
public:
void fight(Creature& c) {c.fightwho(*this);}
};
class Warrior: public Creature{
void fightwho(Warrior& w) {cout<<"Warrior versus Warrior"<<endl; }
void fightwho(Monster& m) {cout<<"Monster versus Warrior"<<endl; }
public:
void fight(Creature& c) {c.fightwho(*this);}
};
int main()
{
Warrior w;
Monster m;
w.fight(m);
}
This results in compiler error, which I foresee:
ex12_10.cpp: In member function ‘virtual void Monster::fight(Creature&)’: ex12_10.cpp:17:30: error: ‘class Creature’ has no member named ‘fightwho’
ex12_10.cpp: In member function ‘virtual void Warrior::fight(Creature&)’: ex12_10.cpp:24:29: error: ‘class Creature’ has no member named ‘fightwho’
But I don't know how to proceed from here... Please help.
Well, obviously, you really don't have fightwho declared in your Creature class, so you need to declare it there, and declare it as virtual.
Double dispatch works in a way that for call (this assumes Warrior& w = ..., not Warrior w):
w.fight(m);
First the virtual mechanism will chose Warrior::fight instead of Monster::fight and then the overloading mechanism will pick Monster::fightwho(Warrior& m) instead of Warrior::fightwho(Warrior& m). Note that it would make more sense if you would have:
Warrior w;
Monster m;
Creature& c1 = w;
Creature& c2 = m;
c1.fight(c2); // not w.fight(m)
Therefore, the method which will eventually be called will be dispatched according to type of the object on which you call it and type of the object sent as argument, i.e. double dispatch
Additionally, please note that this might not be the best example as your types are members of the same hierarchy. Visitor design pattern is a good example of double dispatch implementations in languages which don't support it as first class citizens (i.e. C++ and derivatives: Java, C#...)
As #CrazyCasta correctly notes, when your class hierarchy starts to grow, this approach becomes much harder to maintain and can result in explosion of number of methods, so choose carefully...
My contribution to above answers is providing well-tested example in order to clarify double dispatch concept in reality. If you review the below code you will find the answer of how can I implement by myself.
#include <iostream>
using namespace std;
class A;
class A1;
class A2;
class B1;
class B2;
class B {
public:
// dispatcher function to A
virtual void collide(const A& a) const = 0;
// actual collision logic B with types of A
virtual void collide(const A1& a) const = 0;
virtual void collide(const A2& a) const = 0;
};
class A {
public:
// dispatcher function to B
virtual void collide(const B& b) const = 0;
// actual collision logic A with types of B
virtual void collide(const B1& b) const = 0;
virtual void collide(const B2& b) const = 0;
};
class A1 : public A {
public:
void collide(const B& b) const {
// dispatch to b
b.collide(*this);
}
void collide(const B1& b) const {
cout << "collision with B1 and A1" << endl;
}
void collide(const B2& b) const {
cout << "collision with B2 and A1" << endl;
}
};
class A2 : public A {
public:
void collide(const B& b) const {
// dispatch to a
b.collide(*this);
}
void collide(const B1& b) const {
cout << "collision with B1 and A2" << endl;
}
void collide(const B2& b) const {
cout << "collision with B2 and A2" << endl;
}
};
class B1 : public B {
public:
void collide(const A& b) const {
b.collide(*this);
}
void collide(const A1& b) const {
cout << "collision with A1 Bnd B1" << endl;
}
void collide(const A2& b) const {
cout << "collision with A2 Bnd B1" << endl;
}
};
class B2 : public B {
public:
void collide(const A& a) const {
a.collide(*this);
}
void collide(const A1& a) const {
cout << "collision with A1 Bnd B2" << endl;
}
void collide(const A2& a) const {
cout << "collision with A2 Bnd B2" << endl;
}
};
int main() {
A* a = new A1();
B* b = new B2();
// first dispatch is done by polymorphism ( a is resolved as a A1 )
// second dispatch is done in collide function by the function overloading
// ( in collide function we are sending A1 to collide function of B )
a->collide(*b);
}
Consider using a templated function to determine the type of the second object (RTTI) so that most code duplicating can be avoided, like this:
#include <iostream>
#include <vector>
struct Unit // abstract Unit superclass
{
virtual void interact(Unit *u2)=0;
};
template<typename U1,typename U2>
void interact(U1 u1,U2 u2);
struct UnitA : public Unit
{
void interact(Unit *u2) { ::interact(this, u2); }
};
struct UnitB : public Unit
{
void interact(Unit *u2) { ::interact(this, u2); }
};
int main()
{
std::vector<Unit*> units={new UnitA(), new UnitB(), new UnitA(), new UnitB()}; // all the units
// make each pair of units interact with eachother once
for (int i1=0; i1<units.size(); i1++)
for (int i2=i1+1; i2<units.size(); i2++)
units[i1]->interact( units[i2] );
}
template<typename U1,typename U2>
void interact(U1 u1,U2 u2) // takes care of the RTTI work
{
if (auto dc=dynamic_cast<UnitA*>(u2)) interact(*u1, *dc);
else if (auto dc=dynamic_cast<UnitB*>(u2)) interact(*u1, *dc);
else { std::cerr<< "Unkown subclass\n"; exit(1); }
}
// now we can handle the interaction for each subclass permutation:
void interact(UnitA& u1,UnitA& u2) { std::cout<<"UnitA-UnitA\n"; }
void interact(UnitA& u1,UnitB& u2) { std::cout<<"UnitA-UnitB\n"; }
void interact(UnitB& u1,UnitA& u2) { std::cout<<"UnitB-UnitA\n"; }
void interact(UnitB& u1,UnitB& u2) { std::cout<<"UnitB-UnitB\n"; }
The output is a print for the interaction between each pair of units in the vector:
UnitA-UnitB
UnitA-UnitA
UnitA-UnitB
UnitB-UnitA
UnitB-UnitB
UnitA-UnitB
However, this approach is slow because of run-time type checks and because a vector of pointers results in poor caching. For high-performance games consider a more "data oriented design".
If you want to do this you will need to use RTTI. You will need to check the type of the thing being passed in. In general this is not the best design pattern to be used if you can avoid it. If you want to interact two objects you generally want to use the standard interface of another. For instance you might say creature.attack(other_creature) and attack might query the defense of the other creature, and based on that and it's own stats post an hp update to the other_creature.
Related
I not sure how to ask this but basically i pass a base class as an parameter and if the argument is a derived class from the base class i want to be able to access properties only in the derived class
class A{
public:
bool isB = false;
int x = 69;
}
class B : public A{
public:
bool isB = true;
int y = 420;
}
void Print(A c){
if (c.isB)
cout << c.y << endl; //this will error as the class A has no y even though i will pass class B as an argument
else
cout << c.x << endl;
}
A a;
B b;
Print(a);
Print(b);
My recommendation is that you use polymorphism by creating a virtual "print" function that your global Print function calls:
class A
{
int x = 69;
public:
virtual ~A() = default; // Needed for polymorphic classes
virtual void print(std::ostream& out) const
{
out << x;
}
};
class B : public A
{
int y = 420;
public:
void print(std::ostream& out) const override
{
out << y;
}
};
void Print(A const& o)
{
o.print(std::cout);
std::cout << std::endl;
}
int main()
{
A a;
B b;
Print(a);
Print(b);
}
You need to define a virtual function "bool amIaB()" in both base and derived class that returns "isB".
I have a pair of base/derived classes which are almost identical, but not quite.
I could simply copy all the code of Base1 > Derived1 to create Base2 > Derived2, but that would be ugly, and would require making almost any modification twice.
Question: How can I share as much code as possible between the two pairs, to avoid code duplication?
I tried to create a small toy example that has most features of the actual problem. I want to avoid having duplicate code for the identical part of the interface of D1 and D2. If you want to see more of the actual problem, scroll to the end of the question.
#include <iostream>
using namespace std;
//////////// 1st PAIR ////////////
class B1 {
protected:
string name;
public:
B1() : name("B1") { } // constructors are different between B1 and B2
void speak() { cout << name << endl; } // identical between B1 and B2
};
template<typename T>
class D1 : public B1 {
T x; // identical between D1 and D2
public:
D1(const T &a) { x = a + name.size(); } // refers to base class member
int getX() { return x; } // identical between D1 and D2
int nameLength() { return name.size(); } // accesses member of B, identical between D1 and D2
// differences between D1 and D2 follow:
int add(int i, int j) { return i+j; } // different signature between D1 and D2
void more() {} // not present in D1
};
//////////// 2nd PAIR ////////////
class B2 {
protected:
string name;
public:
B2() : name("B2") { }
void speak() { cout << name << endl; }
};
template<typename T>
class D2 : public B2 {
T x; // identical between D1 and D2
public:
D2(const T &a) { x = a + name.size(); }
int getX() { return x; } // identical between D1 and D2
int nameLength() { return name.size(); } // accesses member of B, identical between D1 and D2
int add(int i, int j, int k) { return i+j+k; } // different signature between D1 and D2
};
// this is just to test that the program compiles and works
int main() {
D1<int> d1(5);
D2<long> d2(6l);
cout << d1.getX();
cout << d1.nameLength();
return 0;
}
The interface of B1 and B2 can be shared by making them inherit from a class BInterface.
It was suggested to me to use multiple inheritance to be able to do the same with D1 and D2, through an additional base class DInterface. Furthermore, it was suggested I try to use the curiously recurring template pattern to allow this additional base class to access the members of D1 and D2. My attempt at doing this follows. I find it a bit complicated, and I would like to know if this is a reasonable approach, and whether there is a better way to do the same.
#include <iostream>
using namespace std;
//////////// COMMON INTERFACES ////////////
class BInterface {
protected:
string name;
BInterface(const string &n) : name(n) { }
public:
void speak() { cout << name << endl; }
};
template<typename D>
class DInterface {
private:
D &derived() { return *static_cast<D *>(this); }
protected:
DInterface() {}
public:
int getX() { return derived().x; }
int nameLength() { return derived().name.size(); }
};
//////////// 1st PAIR ////////////
class B1 : public BInterface {
public:
B1() : BInterface("B1") { } // constructors are different between B1 and B2
};
template<typename T>
class D1 : public B1, public DInterface< D1<T> > {
friend class DInterface< D1<T> >;
T x; // identical between D1 and D2
public:
D1(const T &a) { x = a + name.size(); } // refers to base class member
int add(int i, int j) { return i+j; } // different signature between D1 and D2
void more() {} // not present in D1
};
//////////// 2nd PAIR ////////////
class B2 : public BInterface {
public:
B2() : BInterface("B2") { }
};
template<typename T>
class D2 : public B2, public DInterface< D2<T> > {
friend class DInterface< D2<T> >;
T x; // identical between D1 and D2
public:
D2(const T &a) { x = a + name.size(); }
int add(int i, int j, int k) { return i+j+k; } // different signature between D1 and D2
};
// this is just to test that the program compiles and works
int main() {
D1<int> d1(5);
D2<long> d2(6l);
cout << d1.getX();
cout << d1.nameLength();
return 0;
}
Since several people have commented that this is too broad, and that the context from my actual problem is lost, below I will describe the actual problem:
Mathematica has a C extension API. Certain data types, such as dense or sparse arrays or images can be manipulated in C. I am working on a much easier to use C++ interface. The system also includes in interface generator: a lot of glue code is automatically generated based on a symbolic representation of a C++ class interface in Mathematica. Here's an older version of the system.
I am now working on handling images. Mathematica has Image and Image3D, distinct expressions for 2D and 3D images. Image can also have different pixel types, such as byte, 16-bit, floating point, etc.
The C API uses a single representation for all of these, including 2D and 3D images, called MImage (a pointer type, multiple MImages may point to the same image in memory).
It is convenient for have separate classes for 2D and 3D images in C++, and also to template these on the pixel type. These correspond to the D1 and D2 classes above. However, in some cases, it is useful to operate with "generic" images that may have any pixel type (pixels can't be accessed in this case, but we can do other things with the images too). This is why I also have the base classes B1 and B2.
Here's the implementation of 2D image references so far (this is not done and it will change). I still need to add 3D images, which will share a lot of code.
This solution factors out the concepts of having a name and having a value through a base class that has a name.
If the individual components of the derived classes do not depend on each other then this kind of inheritance composition is relatively easy to maintain.
If the concerns of the base classes are interdependent then you'll have to use CRTP and marshal calls via the derived class.
#include <iostream>
using namespace std;
// factor out common parts
struct NamedThing
{
NamedThing(std::string &&name) : name(std::move(name)) {}
NamedThing(std::string const& name) : name(name) {}
void speak() { cout << name << endl; }
std::size_t nameLength() const { return name.size(); }
private:
std::string name;
};
template<class T, class Base>
struct NamedValue : public Base
{
T x; // identical between D1 and D2
public:
NamedValue(T const& v)
: Base()
, x(this->nameLength())
{}
T getX() { return x; } // identical between D1 and D2
};
//////////// 1st PAIR ////////////
class B1 : public NamedThing
{
public:
B1() : NamedThing("B1") { } // constructors are different between B1 and B2
};
template<typename T>
class D1 : public NamedValue<T, B1> {
using inherited = NamedValue<T, B1>;
public:
D1(const T &a)
: inherited(a)
{
}
// differences between D1 and D2 follow:
int add(int i, int j) { return i+j; } // different signature between D1 and D2
void more() {} // not present in D1
};
//////////// 2nd PAIR ////////////
class B2 : public NamedThing
{
public:
B2() : NamedThing("B2") { }
};
template<typename T>
class D2 : public NamedValue<T, B2> {
using inherited = NamedValue<T, B2>;
public:
D2(const T &a)
: inherited(a)
{
}
int add(int i, int j, int k) { return i+j+k; } // different signature between D1 and D2
};
// this is just to test that the program compiles and works
int main() {
D1<int> d1(5);
D2<long> d2(6l);
cout << d1.getX();
cout << d1.nameLength();
return 0;
}
Where you want to inherit for code reuse you can use private inheritance. With private inheritance the derived classes are blocked from being cast to their base classes.
#include <string>
#include <iostream>
class super
{
std::string name_;
public:
super( std::string n ): name_(n) {}
virtual ~super(){}
std::string name() const { return this->name_; }
void name( std::string n ) { this->name_ = n; }
};
class base1: private super
{
int vertices_;
public:
base1( std::string n, int v ): super( n ), vertices_( v ) {}
virtual ~base1() {}
using super::name; // make both name methods accessible
int vertices() const { return this->vertices_; }
void vertices( int v ) { this->vertices_ = v; }
};
class base2: private super
{
std::string surname_;
public:
base2( std::string n, std::string s ): super( n ), surname_( s ) {}
virtual ~base2() {}
// to make only one name method accessible
std::string name() const { return this->super::name(); }
std::string surname() const { return this->surname_; }
};
// class derived1: public base1 { ... };
// class derived2: public base2 { ... };
int main()
{
base1 v1( "triangle", 3 );
base2 v2( "john", "doe" );
std::cout << "base1: " << v1.name() << " " << v1.vertices() << "\n";
std::cout << "base2: " << v2.name() << " " << v2.surname() << "\n";
v1.name( "square" );
v1.vertices( 4 );
std::cout << "base1: " << v1.name() << " " << v1.vertices() << "\n";
//v2.name( "jane" ); // illegal code
//super *p1 = &v1; // illegal code
//super *p2 = &v2; // illegal code
//derived1 d1(...);
//derived2 d2(...);
//base1 *p1 = &d1; // allowed
//base2 *p2 = &d2; // allowed
//derived1 *p1 = dynamic_cast< derived1* >((super*)&d2); // Not allowed
return 0;
}
With private inheritance you cannot directly access any base class methods outside the derived class. You have two options to allow this: (1) In base1 we use a public using statement to make the two name methods accessible. (2) In base2 we only want one of the name functions so we write a stub method that calls the super class method (NOTE: as this is inline it should result in the same assembly code as the using method).
I have two base classes and derivered versions that overload / override certain parts like this:
class base
{
public:
int X = 1;
};
class deriv : public base
{
public:
int X = 2;
};
class A
{
public:
base K;
virtual void doSmth()
{
std::cout << "A" << std::endl;
smthElse();
}
virtual void smthElse()
{
std::cout << K.X << std::endl;
}
};
class B : public A
{
public:
deriv K;
void doSmth()
{
std::cout << "B" << std::endl;
smthElse();
}
};
the application looks like this
int main()
{
A instanceA;
B instanceB;
instanceA.doSmth();
instanceB.doSmth();
getchar();
return 0;
}
And the output therefore is X=1 for both instances A and B. I was wondering why that is.
A uses base (X=1) and B uses deriv (X=2). deriv overloads X and B overloads K. Is this because the function smthElse() is only defined in A, thus A can't know about the existance of the overloaded variable K?
If so, is there a way for the function smthElse() to use the overloaded variable K?
I found the using keyword but also adding a using A::smthElse; in B won't change the behaviour of X not being printed as 2. The only way I can achieve this is by copying the function smthElse() from A and insert it into B.
Is there a different way to achieve what I'm looking for? Since it seems like an overkill to copy'n'paste the same function into B just to use an overridden variable.
Thanks in advance!
instanceB has two variables named K, A::K and B::K. However, the base class, A, only knows about one K, A::K.
That explains the output.
If so, is there a way for the function smthElse() to use the overloaded variable K?
Yes, you can do that by adding a virtual function in A that returns a reference to base and adding a virtual function in base that returns a reference to i.
class base
{
public:
int& getX( return X;}
private:
int X = 1;
};
class deriv : public base
{
public:
int& getX( return X;}
private:
int X = 2;
};
class A
{
public:
base& getK() { return K; }
virtual void doSmth()
{
std::cout << "A" << std::endl;
smthElse();
}
virtual void smthElse()
{
std::cout << getK().getX() << std::endl;
// ^^^^^^^^^^^^^ use the virtual functions
}
public:
base K;
};
class B : public A
{
public:
deriv& getK(){ return K; }
void doSmth()
{
std::cout << "B" << std::endl;
smthElse();
}
public:
base K;
};
PS I hope this is just curiosity and you don't write production code with such style. You will end up confusing yourself and anybody who tries to understand your code.
When you write
virtual void smthElse()
{
std::cout << K.X << std::endl;
}
smthElse is virtual
K is not (a member variable could not be virtual: it has no meaning for an attribute).
In other terms, it means that B::smthElse will ovevrride A::smthElse but B::K and A::K are two distinct, unrelated and independent variables.
When smthElse is called in the context of a B, K still means A::K.
As a solution, you might create a virtual accessor to Ks:
class base { ...};
class deriv{ ...};
class A
{
base K;
public:
virtual const base& theK() { return K; }
virtual void smthElse() { std::cout << theK().X << "\n"; }
};
class B : public A
{
deriv K;
public:
virtual const base& theK() { return K; }
};
When B{}.smthElse() is called, it will call B::theK() which will return B::K (a deriv instance).
I've a parent class with 2 or more child class deriving from it. The number of different child classes may increase in future as more requirements are presented, but they'll all adhere to base class scheme and will contain few unique methods of their own. Let me present an example -
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class B{
private: int a; int b;
public: B(const int _a, const int _b) : a(_a), b(_b){}
virtual void tell(){ std::cout << "BASE" << std::endl; }
};
class C : public B{
std::string s;
public: C(int _a, int _b, std::string _s) : B(_a, _b), s(_s){}
void tell() override { std::cout << "CHILD C" << std::endl; }
void CFunc() {std::cout << "Can be called only from C" << std::endl;}
};
class D : public B{
double d;
public: D(int _a, int _b, double _d) : B(_a, _b), d(_d){}
void tell() override { std::cout << "CHILD D" << std::endl; }
void DFunc() {std::cout << "Can be called only from D" << std::endl;}
};
int main() {
std::vector<std::unique_ptr<B>> v;
v.push_back(std::make_unique<C>(1,2, "boom"));
v.push_back(std::make_unique<D>(1,2, 44.3));
for(auto &el: v){
el->tell();
}
return 0;
}
In the above example tell() method would work correctly since it is virtual and overrided properly in child classes. However for now I'm unable to call CFunc() method and DFunc() method of their respective classes. So I've two options in my mind -
either packup CFunc() and friends inside some already defined virtual method in child class so that it executes together. But I'll loose control over particular execution of unique methods as their number rises.
or provide some pure virtual methods in base class, which would be like void process() = 0 and let them be defined in child classes as they like. Would be probably left empty void process(){} by some and used by some. But again it doesn't feels right as I've lost return value and arguments along the way. Also like previous option, if there are more methods in some child class, this doesn't feels right way to solve.
and another -
dynamic_cast<>?. Would that be a nice option here - casting back parent's pointer to child's pointer (btw I'm using smart pointers here, so only unique/shared allowed) and then calling the required function. But how would I differentiate b/w different child classes? Another public member that might return some unique class enum value?
I'm quite unexperienced with this scenario and would like some feedback. How should I approach this problem?
I've a parent class with 2 or more child class deriving from it... But I'll loose control over particular execution of unique methods as their number rises.
Another option, useful when the number of methods is expected to increase, and the derived classes are expected to remain relatively stable, is to use the visitor pattern. The following uses boost::variant.
Say you start with your three classes:
#include <memory>
#include <iostream>
using namespace std;
using namespace boost;
class b{};
class c : public b{};
class d : public b{};
Instead of using a (smart) pointer to the base class b, you use a variant type:
using variant_t = variant<c, d>;
and variant variables:
variant_t v{c{}};
Now, if you want to handle c and d methods differently, you can use:
struct unique_visitor : public boost::static_visitor<void> {
void operator()(c c_) const { cout << "c" << endl; };
void operator()(d d_) const { cout << "d" << endl; };
};
which you would call with
apply_visitor(unique_visitor{}, v);
Note that you can also use the same mechanism to uniformly handle all types, by using a visitor that accepts the base class:
struct common_visitor : public boost::static_visitor<void> {
void operator()(b b_) const { cout << "b" << endl; };
};
apply_visitor(common_visitor{}, v);
Note that if the number of classes increases faster than the number of methods, this approach will cause maintenance problems.
Full code:
#include "boost/variant.hpp"
#include <iostream>
using namespace std;
using namespace boost;
class b{};
class c : public b{};
class d : public b{};
using variant_t = variant<c, d>;
struct unique_visitor : public boost::static_visitor<void> {
void operator()(c c_) const { cout << "c" << endl; };
void operator()(d d_) const { cout << "d" << endl; };
};
struct common_visitor : public boost::static_visitor<void> {
void operator()(b b_) const { cout << "b" << endl; };
};
int main() {
variant_t v{c{}};
apply_visitor(unique_visitor{}, v);
apply_visitor(common_visitor{}, v);
}
You can declare interfaces with pure methods for each device class. When you define a specific device implementation, you inherit only from the interfaces that make sense for it.
Using the interfaces that you define, you can then iterate and call methods which are specific to each device class.
In the following example I have declared a HardwareInterface which will be inherited by all devices, and an AlertInterface which will be inherited only by hardware devices that can physically alert a user. Other similar interfaces can be defined, such as SensorInterface, LEDInterface, etc.
#include <iostream>
#include <memory>
#include <vector>
class HardwareInteface {
public:
virtual void on() = 0;
virtual void off() = 0;
virtual char read() = 0;
virtual void write(char byte) = 0;
};
class AlertInterface {
public:
virtual void alert() = 0;
};
class Buzzer : public HardwareInteface, public AlertInterface {
public:
virtual void on();
virtual void off();
virtual char read();
virtual void write(char byte);
virtual void alert();
};
void Buzzer::on() {
std::cout << "Buzzer on!" << std::endl;
}
void Buzzer::off() {
/* TODO */
}
char Buzzer::read() {
return 0;
}
void Buzzer::write(char byte) {
/* TODO */
}
void Buzzer::alert() {
std::cout << "Buzz!" << std::endl;
}
class Vibrator : public HardwareInteface, public AlertInterface {
public:
virtual void on();
virtual void off();
virtual char read();
virtual void write(char byte);
virtual void alert();
};
void Vibrator::on() {
std::cout << "Vibrator on!" << std::endl;
}
void Vibrator::off() {
/* TODO */
}
char Vibrator::read() {
return 0;
}
void Vibrator::write(char byte) {
/* TODO */
}
void Vibrator::alert() {
std::cout << "Vibrate!" << std::endl;
}
int main(void) {
std::shared_ptr<Buzzer> buzzer = std::make_shared<Buzzer>();
std::shared_ptr<Vibrator> vibrator = std::make_shared<Vibrator>();
std::vector<std::shared_ptr<HardwareInteface>> hardware;
hardware.push_back(buzzer);
hardware.push_back(vibrator);
std::vector<std::shared_ptr<AlertInterface>> alerters;
alerters.push_back(buzzer);
alerters.push_back(vibrator);
for (auto device : hardware)
device->on();
for (auto alerter : alerters)
alerter->alert();
return 0;
}
Interfaces can be even more specific, as per individual sensor type: AccelerometerInterface, GyroscopeInterface, etc.
While what you ask is possible, it will either result in your code scattered with casts, or functions available on classes that make no sense. Both are undesirable.
If you need to know if it's a class C or D, then most likely either storing it as a B is wrong, or your interface B is wrong.
The whole point of polymorphism is that the things using B is that they don't need to know exactly what sort of B it is. To me, it sounds like you're extending classes rather than having them as members, ie "C is a B" doesn't make sense, but "C has a B does".
I would go back and reconsider what B,C,D and all future items do, and why they have these unique functions that you need to call; and look into if function overloading is what you really want to do. (Similar to Ami Tavory suggestion of visitor pattern)
you can use unique_ptr.get() to get the pointer in Unique Pointer,And the use the pointer as normall. like this:
for (auto &el : v) {
el->tell();
D* pd = dynamic_cast<D*>(el.get());
if (pd != nullptr)
{
pd->DFunc();
}
C* pc = dynamic_cast<C*>(el.get());
if (pc != nullptr)
{
pc->CFunc();
}
}
and the result is this:
CHILD C
Can be called only from C
CHILD D
Can be called only from D
You should use your 1st approach if you can to hide as much type-specific implementation details as possible.
Then, if you need public interfaces you should use virtual funtions (your 2nd approach), and avoid dynamic_cast (your 3rd approach). Many theads can tell you why (e.g. Polymorphism vs DownCasting). and you already mentioned one good reason, which is you shouldn't really check for the object type ...
If you have a problem with virtual functions because your drived classes have too many unique public interfaces, then it's not IS-A relationship and it's time to review your design. For example, for shared functionality, consider composition, rather than inheritance ...
There's been a lot of comments (in OP and Ami Tavory's answer) about visitor pattern.
I think it is and acceptable answer here (considering the OP question), even if visitor pattern has disadvantages, it also has advantages (see this topic: What are the actual advantages of the visitor pattern? What are the alternatives?). Basically, if you'll need to add a new child class later, the pattern implementation will force you to consider all cases where specific action for this new class has to be taken (compiler will force you to implement the new specific visit method for all your existing visitor child classes).
An easy implementation (without boost):
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class C;
class D;
class Visitor
{
public:
virtual ~Visitor() {}
virtual void visitC( C& c ) = 0;
virtual void visitD( D& d ) = 0;
};
class B{
private: int a; int b;
public: B(const int _a, const int _b) : a(_a), b(_b){}
virtual void tell(){ std::cout << "BASE" << std::endl; }
virtual void Accept( Visitor& v ) = 0; // force child class to handle the visitor
};
class C : public B{
std::string s;
public: C(int _a, int _b, std::string _s) : B(_a, _b), s(_s){}
void tell() override { std::cout << "CHILD C" << std::endl; }
void CFunc() {std::cout << "Can be called only from C" << std::endl;}
virtual void Accept( Visitor& v ) { v.visitC( *this ); }
};
class D : public B{
double d;
public: D(int _a, int _b, double _d) : B(_a, _b), d(_d){}
void tell() override { std::cout << "CHILD D" << std::endl; }
void DFunc() {std::cout << "Can be called only from D" << std::endl;}
virtual void Accept( Visitor& v ) { v.visitD( *this ); }
};
int main() {
std::vector<std::unique_ptr<B>> v;
v.push_back(std::make_unique<C>(1,2, "boom"));
v.push_back(std::make_unique<D>(1,2, 44.3));
// declare a new visitor every time you need a child-specific operation to be done
class callFuncVisitor : public Visitor
{
public:
callFuncVisitor() {}
virtual void visitC( C& c )
{
c.CFunc();
}
virtual void visitD( D& d )
{
d.DFunc();
}
};
callFuncVisitor visitor;
for(auto &el: v){
el->Accept(visitor);
}
return 0;
}
Live demo: https://ideone.com/JshiO6
Dynamic casting is the tool of absolute last resort. It is usually used when you are trying to overcome a poorly designed library that cannot be modified safely.
The only reason to need this sort of support is when you require parent and child instances to coexist in a collection. Right? The logic of polymorphism says all specialization methods that cannot logically exist in the parent should be referenced from within methods that do logically exist in the parent.
In other words, it is perfectly fine to have child class methods that don't exist in the parent to support the implementation of a virtual method.
A task queue implementation is the quintessential example (see below)
The special methods support the primary run() method. This allows a stack of tasks to be pushed into a queue and executed, no casts, no visitors, nice clean code.
// INCOMPLETE CODE
class Task
{
public:
virtual void run()= 0;
};
class PrintTask : public Task
{
private:
void printstuff()
{
// printing magic
}
public:
void run()
{
printstuff();
}
};
class EmailTask : public Task
{
private:
void SendMail()
{
// send mail magic
}
public:
void run()
{
SendMail();
}
};
class SaveTask : public Task
private:
void SaveStuff()
{
// save stuff magic
}
public:
void run()
{
SaveStuff();
}
};
Here's a "less bad" way of doing it, while keeping it simple.
Key points:
We avoid losing type information during the push_back()
New derived classes can be added easily.
Memory gets deallocated as you'd expect.
It's easy to read and maintain, arguably.
struct BPtr
{
B* bPtr;
std::unique_ptr<C> cPtr;
BPtr(std::unique_ptr<C>& p) : cPtr(p), bPtr(cPtr.get())
{ }
std::unique_ptr<D> dPtr;
BPtr(std::unique_ptr<D>& p) : dPtr(p), bPtr(dPtr.get())
{ }
};
int main()
{
std::vector<BPtr> v;
v.push_back(BPtr(std::make_unique<C>(1,2, "boom")));
v.push_back(BPtr(std::make_unique<D>(1,2, 44.3)));
for(auto &el: v){
el.bPtr->tell();
if(el.cPtr) {
el.cPtr->CFunc();
}
if(el.dPtr) {
el.dPtr->DFunc();
}
}
return 0;
}
I want to make some "duel" with two "units".
I write class "duel" that constructs from two "units".
But some kind of "unit" is special (inherited from units) like heroes, bosses etc. And they want to use special strikes during battle.
But actually class "duel" doesn't know who is hero, or who is pure unit.
Code looks like this:
#include <iostream>
class unit{
public:
unit(){};
virtual void make_hit(){
std::cout<<"pure hit\n";
}
};
class hero:public unit {
public:
hero():unit(){};
void make_hit(){
std::cout<<"SUPER hit\n";
}
};
class duel {
unit *a, *b;
public:
duel(unit _a, unit _b):a(&_a),b(&_b){};
void start (){
a->make_hit();
b->make_hit();
}
};
int main(){
duel(unit(),hero()).start();
return 0;
}
I have two main problem.
First - I use refers to temporary objects in constructor. That objects illegal when duel::duel() finished.
Second - my hero turned into pure unit, and doesn't use "SUPER hit"
Is it possible fix it in elegant way (without changing call in main())?
Due to slicing, it's better to always use polymorphism together with
smart-pointers. This would be a possible design:
#include <iostream>
#include <memory>
#include <utility>
using namespace std;
class unit_base
{
public:
virtual ~unit_base() = default;
virtual void make_hit() =0;
};
class unit : public unit_base
{
public:
unit() = default;
virtual void make_hit() override
{
cout << "pure hit" << endl;
}
};
class hero : public unit_base
{
public:
hero() = default;
virtual void make_hit() override
{
cout << "SUPER hit" << endl;
}
};
class duel
{
public:
duel( shared_ptr<unit_base> a, shared_ptr<unit_base> b )
: a(a), b(b)
{}
void start()
{
auto aa = a.lock();
auto bb = b.lock();
if( aa && bb )
{
aa->make_hit();
bb->make_hit();
} else {
cout << "duelist expired" << endl;
}
}
private:
weak_ptr<unit_base> a, b;
};
int main()
{
// use with temporarys
duel{ make_shared<unit>(), make_shared<hero>() }.start();
cout << "-------------" << endl;
// use with variables
auto u = make_shared<unit>();
auto h = make_shared<hero>();
duel d{h,u};
d.start();
cout << "-------------" << endl;
// try to use with expired duelists
u.reset();
d.start();
}
Also remember to always have a virtual destructor in your base-class.
In
duel(unit _a, unit _b):a(&_a),b(&_b){};
You are slicing the objects as you are passing by value. To fix this you can take in pointers in your constructor
duel(unit* _a, unit* _b):a(_a),b(_b){};
And then you will need to change main() to create to objects and pass them to duel
int main(){
unit npc;
hero bob;
duel d(&npc,&bob);
d.start();
return 0;
}
The only way I have found for myself in C++ is make all combination of constructor in "duel" class. This solution not so elegant, need changing in "duel" code when added new class, and also this have overhead.
~duel(){
delete a;
delete b;
}
#define _cc(t1, t2) duel(t1 _a, t2 _b) : a(new t1 (_a)), b (new t2(_b)){}
_cc(unit,unit);
_cc(hero,unit);
_cc(unit,hero);
_cc(hero,hero);
#undef _cc
I also tried use template for this, but I can't find a way make automatic type determination.