Hello I am new to C++ and I am trying to understand the subject observer pattern. Here is what I have been working on based on an example form the design patters book.

#include <vector>
#include <iostream>

class Subject;

class Observer 
{
public:
    virtual void notify(Subject* s) = 0;
};

class Subject
{
    std::vector<Observer*> *observers;
protected:
    void notify_observers()
    {        
        std::vector<Observer*>::iterator iter;
        for (iter = observers->begin(); iter != observers->end(); ++iter)
            (*iter)->notify(this);
    }
    
public:
    void register_observer(Observer* o)
    {
        observers->push_back(o);
    }
};

class Horn : public Observer
{
public:
    virtual void notify(Subject* s)
    {
        std::cout << "Horn with id " << s->get_alarm_id << " is sounding\n";
    }
};

class Alarm : public Subject
{
public:
    Alarm()
    {
        std::cout << "alarm created" << "\n";
    }
    
    void triggerd()
    {
        std::cout << "The alarm has been triggerd" << "\n";
        notify_observers();
    }
    
    int const get_alarm_id(){ return 100; }
};

int main ()
{
    Alarm a = Alarm();
    Horn h = Horn();
    a.register_observer(&h);
    a.triggerd();
    return 0;
}

Basically I want to push the subject class to the observer so the observer can get information form the subject. However When the notify method is called it gets the subject object not the alarm object. Can someone tell me how to ensure that the alarm object is getting passed.

That is not quite true, when the notify method is called it gets a Subject pointer to some object. You know that object can definitely not be a Subject because Subject is abstract and so can not be instantiated.

You have been passed a base class (Subject) pointer to a derived class (possibly an Alarm).

You can use the typeid operator or dynamic casting to determine the type of the object being pointed to. Obviously you will not be able to call any derived class functions through a base class pointer so some conversion may be required.

Thanks for the reply Banfa. However I think I might need a little more information. I looked in to using typeid to identify the object. However it seems to be indemnifying the object as...

P7Subject

Also I have attempted to perform a dynamic_cast on the pointer but I get an error.

subject_observer.cpp:60: error: cannot dynamic_cast ‘s’ (of type ‘class Subject*’) to type ‘class Alarm*’ (source type is not polymorphic)

Is there something major I am missing? Here is the code I wrote to attempt these tasks.

class Horn : public Observer
{
public:
    virtual void notify(Subject* s)
    {
        Alarm* a;
        a = dynamic_cast<Alarm*>(s);
        std::cout << typeid(s).name() << "\n";
    }
};

Thanks again for your help.

Edited 6 Years Ago by stewartmatheson: n/a

The problem is that you class Subject has no virtual methods so has no vtable to allow the dynamic cast to happen.

The esiest way to solve this is to add a virtual deestructor virtual ~Subject() {}; to Subject. If you are going to sub-class from it and use base class pointers the destructor should be virtual anyway to ensure correct operation should you delete through the base class pointer.

Ah! That's done it. Thanks a lot. Do you have any suggestions on the best way to integrate the cast with in the base classes so I can keep this solution generic?

Basically you can't integrate that cast. You can do something convoluted using the visitor design pattern that ends up calling a method on Horn with a pointer to Alarm but I am not sure that that is any better than just doing a dynamic cast.

You could create a template function in Observer to do the cast but that is barely any better than just calling dynamic_cast directly.

You have the generic solution.

Many concrete observers (Horn in your case) maintain a reference to the concrete subject they are observing, after all often observers wish to access subjects at times other than when the subject is updated. This reference can have the concrete type so have access to the subjects public interface. The pointer passed in notify is just used to verify that the update is about the specific object the observer is interested in.

In fact in this scenario passing the subject in the notify function can be omitted if there is a strict 1 to 1 (or many to 1) relationship between observer and subject.

This article has been dead for over six months. Start a new discussion instead.