Problem Creating an "Interface"

Started by
3 comments, last by RyanZec 16 years, 11 months ago
I am not 100% if this is teh right way of using a word interface so please create me if I am wrong. I am reading the book Head First Design Patterns and trying to follow along with their examples which are written in Java(I am working in C++). This first one is basically we have a base class of Duck, here is the code for it:

#include "fly_behavior.h"
#include "speak_behavior.h"

#ifndef DUCK_H
#define DUCK_H

abstract class Duck
{
public:
	Duck();
	~Duck();
	void PerformFly();
	void PerformSpeak();

protected:
	FlyBehavior * _fly_behavior;
	SpeakBehavior * _speak_behavior;
};

#endif


#include "duck.h"

Duck::Duck()
{

}

Duck::~Duck()
{

}

void Duck::PerformFly()
{
	this->_fly_behavior->Fly();
}

void Duck::PerformSpeak()
{
	this->_speak_behavior->Speak();
}

Know the 2 "interfaces" i have hear is the FlyBehavior class and SpeakBehavior class, here is the code for those:

#ifndef FLY_BEHAVIOR_H
#define FLY_BEHAVIOR_H

class FlyBehavior
{
public:
	void Fly();
};

#endif


#include "fly_behavior.h"

void FlyBehavior::Fly()
{

}



#ifndef SPEAK_BEHAVIOR_H
#define SPEAK_BEHAVIOR_H

class SpeakBehavior
{
public:
	void Speak();
};

#endif


#include "speak_behavior.h"

void SpeakBehavior::Speak()
{

}

Now the reason that the methods in these classes and blank is becuase I should never have to have an object typ eof this class, only objects that are sub classes. from my understanding, which is rusty since i havr been mainly doing PHP code for the past 1 1/2 and not c++, is that if Squeak class is a sub class of SpeakBehavior that to following sode would work:

SpeakBehavior * _speak_behavior = new Squeak()

THis code does work for my ToyDuck class that is a sub class of duck, here is that code:

#include "duck.h"

#ifndef TOY_DUCK_H
#define TOY_DUCK_H

class ToyDuck: public Duck
{
public:
	ToyDuck();
	~ToyDuck();
};

#endif


#include "toy_duck.h"
#include "not_fly.h"
#include "squeak.h"

ToyDuck::ToyDuck()
{
	this->_fly_behavior = new NotFly();
	this->_speak_behavior = new Squeak();
}

ToyDuck::~ToyDuck()
{

}

This code sompiles fine but when i run it and do

#include <iostream>
#include <conio.h>

#include "toy_duck.h"

void main()
{
	std::cout << "DUCK SIMULATOR" << std::endl;
	
	ToyDuck * toy_duck_object = new ToyDuck();

	toy_duck_object->PerformFly();
	toy_duck_object->PerformSpeak();
	
	_getch();
}

the PerformFly and PerformSpeak run the Fly/Speak method from the FlyBehavior and SpeakBehavior and not the NotFly or Squeak, which a sub classes of FlyBehavior and SpeakBehavior, even tho NotFly or Squeak have thier own Fly/Speak methods defined. Can anyone see why these classes are running the superclasses methods and the the child classes methods?
Advertisement
In C++, if you want the correct subclass method to be called when working with a base class pointer, you need to declare the method as "virtual" in the base class.

Also, if you allocate objects with "new" in a constructor, you should delete them in the destructor of the class.
When I declare those function virtual is the base class i get a linking error becuase I don't greate the defination for those function but i should not have to since they are virtual.

Oh an dthnaks for pointing out the memory leak, thanks.
#include "fly_behavior.h"#include "speak_behavior.h"#ifndef DUCK_H#define DUCK_H// "Duck" is not abstractclass Duck{public:        // pass the behavior in the ctor - favour composition over inheritance	Duck(FlyBehavior *, SpeakBehavior*);	~Duck();	void PerformFly();	void PerformSpeak();protected:	FlyBehavior * _fly_behavior;	SpeakBehavior * _speak_behavior;};#endif

#include "duck.h"// use proper constructor initialisersDuck::Duck(FlyBehavior * fly, SpeakBehavior * speak): _fly_behavior(fly),  _speak_behavior(speak){}// remember to delete what you allocateDuck::~Duck(){    delete _fly_behavior;    delete _speak_behavior;}void Duck::PerformFly(){    _fly_behavior->Fly();}void Duck::PerformSpeak(){    _speak_behavior->Speak();}

#include "duck.h"#include "not_fly.h"#include "squeak.h"namespace{    Duck * MakeToyDuck()    {        return new Duck(new NoFly, new Squeak);    }}// "main" should return "int"int main(){	std::cout << "DUCK SIMULATOR" << std::endl;	        // prefer a factory method over inheritance	Duck * toy_duck_object = MakeToyDuck();	toy_duck_object->PerformFly();	toy_duck_object->PerformSpeak();	        delete toy_duck_object;}


The above doesn't reflect your idea of the design, but illustrates the intention. Compare it to the implementation that uses inheritance, which is is given below:

#ifndef DUCK_H#define DUCK_H// "Duck" is an abstract base classclass Duck{public:        // note the virtual destructor	virtual ~Duck();	        void PerformFly();	void PerformSpeak();protected:        // make the ctor protected, i.e. "Duck" cannot be instanciated        Duck(FlyBehavior *, SpeakBehavior *);	FlyBehavior * _fly_behavior;	SpeakBehavior * _speak_behavior;};#endif

#include "duck.h"Duck::Duck(FlyBehavior * fly, SpeakBehavior * speak): _fly_behavior(fly),  _speak_behavior(speak){}Duck::~Duck(){    delete _fly_behavior;    delete _speak_behavior;}void Duck::PerformFly(){    _fly_behavior->Fly();}void Duck::PerformSpeak(){    _speak_behavior->Speak();}

include "duck.h"#ifndef TOY_DUCK_H#define TOY_DUCK_H// the dtor is not necessary, it's already defined in "Duck"class ToyDuck: public Duck{public:        // the ctor is public and omits the behavior arguments	ToyDuck();};#endif

#include "toy_duck.h"#include "not_fly.h"#include "squeak.h"// the ctor invokes the base class' ctor with the appropriate argumentsToyDuck::ToyDuck() : Duck(new NotFly, new Squeak){}


#include <iostream>#include "toy_duck.h"// "main" stays the sameint main(){	std::cout << "DUCK SIMULATOR" << std::endl;	        // note that you can use both "Duck" and "ToyDuck" as your        / object's type. prefer the base class.	Duck * toy_duck_object = new ToyDuck;	toy_duck_object->PerformFly();	toy_duck_object->PerformSpeak();        delete toy_duck_object;}


Finally, your behavior "interfaces" are no real interfaces. Interfaces in C++ should be abstract classes:

#ifndef FLY_BEHAVIOR_H#define FLY_BEHAVIOR_Hclass FlyBehavior{public:        // the "virtual" is required so that calls to the interface        // will dispatch to the derived class. the " = 0" denotes        // an abstract method, i.e. FlyBehavior::Fly() cannot be called        // and FlyBehavoir cannot be instanciated.        // the brackets are the default implementaion, which may be called        // by derived classes using "FlyBehavior::Fly()"	virtual void Fly() = 0 { }};/*   Example:   class NoFly : public FlyBehavior   {        // implement "Fly" by just calling the default implementation.        // only works if a default implementation exists, though.        void Fly()        {            FlyBehavior::Fly();        }   };*/#endif

The same principle applies to "SpeakBehavior". The reason your derived classes didn't work was the fact that the behavoir "interface" methods weren't marked "virtual".

Hope that clears things up a little,
Pat

PS: No guarantees, though - I didn't work with C++ for quite some time [smile]
PPS: Seems I'm a bit slow [lol]
yea, my problems was i forgot i needed to do " = 0" with my virtual function. Thanks

This topic is closed to new replies.

Advertisement