2016-03-15 2 views
1

Я пытаюсь реализовать шаблон Observer для игры, которую я создаю для школьного проекта.Шаблон наблюдателя и наследование: Не вызывать правильную функцию

Я создал 2 виртуальных класса, Наблюдатель и Наблюдаемый.

Observer.h:

Observer.cpp:

#include "stdafx.h" 
#include "Observer.h" 


Observer::Observer() 
{ 
} 

Observer::~Observer() 
{ 
} 

Observable.h:

#ifndef OBSERVEABLE_H 
#define OBSERVEABLE_H 
#include <vector> 
#include "Observer.h" 


class Observable 
{ 
    protected: 
    std::vector<Observer*> observers; 
    public: 
    Observable(); 
    virtual ~Observable(); 
    virtual void attach(Observer *a); 
    virtual void detach(Observer *a); 
    virtual void notify(); 
}; 

#endif 

Observable.cpp:

#include "stdafx.h" 
#include "Observable.h" 

Observable::Observable() 
{ 
} 

Observable::~Observable() 
{ 
} 

void Observable::attach(Observer *a) 
{ 
    observers.push_back(a); 
} 

void Observable::detach(Observer *a) 
{ 
    for (auto it = this->observers.begin(); it < this->observers.end(); it++) 
    { 

      if (*it == a) 
      { 
      this->observers.erase(it); 
      break; 
      } 
    } 
} 

void Observable::notify() 
{ 
    for (int i = 0; i < observers.size(); i++) 
     observers[i]->update(this); 
} 

У меня есть класс Map, который наследуется от Observable, и класс MAPview, который наследуется от наблюдателя (Карта очень долго, я только включены соответствующие функции)

Map.h:

#ifndef MAP_H 
#define MAP_H 
#include "Observable.h" 
#include <iostream> 

class Map : public Observable 
{ 
    public: 
    Map(); 
    ~Map(); 
    void getLatest(); 
    void notify(); 
}; 

#endif 

Карта. каст:

#include "stdafx.h" 
#include "Map.h" 

Map::Map() 
{ 
} 

Map::~Map() 
{ 
} 

void Map::getLatest() 
{ 
    using namespace std; 
    cout << "This is the latest info!" << endl; 
} 

mapView.h:

#ifndef MAP_V_H 
#define MAP_V_H 
#include "Observer.h" 
#include "Map.h" 
#include "Plants.h" 

class mapView : public Observer 
{ 
    public: 
    mapView(); 
    ~mapView(); 
    void update(Map* map); 
}; 

#endif 

mapView.c С.:

#include "stdafx.h" 
#include "mapView.h" 
#include "Map.h" 

mapView::mapView() 
{ 
} 

mapView::~mapView() 
{ 
} 

void mapView::update(Map* map) 
{ 
    map->getLatest(); 
} 

Наконец, мой основной просто создает карту и MAPview, прикрепляет MAPview и вызывает map.notify()

main.cpp:

#include "stdafx.h" 
#include "setUp.h" 
#include "Map.h" 
#include "mapView.h" 

int main() 
{ 
    Map gameMap; 
    mapView view; 
    gameMap.attach(&view); 

    gameMap.notify(); 

    return 0; 
} 

Я бегу в ряд вопросов здесь. Я не могу создать элемент mapView, потому что компилятор говорит, что я никогда не реализовал переопределяющую версию обновления (Observable * ob) .... Я пытался с обновлением (Map * map), но, похоже, что, несмотря на то, что Map наследуется от Observable, похоже, не считается одной и той же подписью, поэтому он не будет компилироваться.

Я попытался изменить функцию mapView :: update(), вместо этого, вместо указателя на Observable, но это не сработает, потому что функция вызывает что-то из класса Map.

Затем я попытался изменить функцию обновления, чтобы НЕ быть виртуальной функцией (с пустой реализацией в виртуальном классе), но, похоже, в любой момент, когда я пытаюсь передать карту для обновления, она будет вызывать функцию базового класса, а не версия mapView. Другими словами, getLatest() никогда не вызывается.

Я сейчас довольно смущен, потому что это похоже на то, как я думал, что полиморфизм сработал. По достоинству оценят некоторую помощь или прозрение!

Спасибо,

+0

Шаблон - ваш друг здесь –

ответ

2

Ваш базовый класс объявляет:

virtual void update(Observable* ob) =0; 

Вы производный класс объявляет:

void update(Map* map); 

Это не то же подпись. Если вы использовали новое ключевое слово override, во время компиляции вы увидите, что вы фактически не переопределили виртуальный метод.

Если вы знаете вы получите только Map с, то вы можете просто использовать static_cast. Но это безопаснее использовать dynamic_cast:

void update(Observable* o) override { // now we're ok 
    if (auto map = dynamic_cast<Map*>(o)) { 
     // okay, got a Map 
     // .... 
    } 
    else { 
     // huh? 
    } 
} 

Супер краткий тип теории экскурс. Типичным правилом для переопределений является co вариант взамен и contra вариант в типе аргумента. Вы можете указать тип возвращаемого типа с более высоким производным или тип аргумента с большей базой. Подумайте об этом так: если у вас есть функция базового класса, которая принимает и возвращает Car* ... ваш аргумент может быть Car* (это именно то, что ожидается), или это может быть Vehicle* (поскольку все, что вы можете сделать с Vehicle, вы можете сделать это с помощью - это все еще работает), но это не может быть SportsCar* (так как вызывающий может передать вам Car, который не является SportsCar и оправданно ожидает, что это сработает!) Это не имеет смысла для производный класс принимать только Map s - вы должны быть в состоянии принять любые Observable s, даже не Map s!

+1

Хотелось бы подчеркнуть, насколько отличным является ключевое слово 'override', вы всегда должны использовать его при работе с виртуальными функциями! –

+0

Спасибо! Это решило мои проблемы. Я думал об использовании динамического броска, как я это видел раньше, но я не использовал его правильно. Вы бы сказали, что это стандартный способ переопределить функцию, если вы знаете, что вы ожидаете, что производный класс будет передан как параметры? Например, при реализации шаблона Observer, например. – waffledave

+0

@ waffledave Я не знаю о «стандарте», но это довольно разумная вещь. – Barry