2015-10-08 23 views
6
class Car { 
    class BaseState { 
     explicit BaseState(Car* vehicle) : mVehicle(vehicle) {} 
     virtual void run() = 0; 

     Car* mVehicle; 
    } 
    class State1 : public BaseState { 
     explicit State1(Car* vehicle) : BaseState(vehicle) {} 
     virtual void run() { 
      // use data of Car 
      ... 
      doSomething(); 
     } 
     virtual void doSomething() { 
     } 
    } 
    class State2 : public BaseState { 
    } 
    ... 
} 

class Convertible: public Car { 
    class State1 : public Car::State1 { 
     explicit State1(Convertible* vehicle) : Car::State1(vehicle) {} 
     virtual void doSomething() { 
      static_cast<Convertible*>(mVehicle)->foldTop(); 
     } 
    } 
    class State2 : public Car::State2 { 
    } 
    ... 
    void foldTop() {} 
} 

Tutti gli Stati sono derivati ​​dal BaseState in modo che abbiano la variabile membro mVehicle per accedere a variabili di classe esterne. Tuttavia, in ogni classe derivata, in tutte le funzioni di ogni stato, è necessario il static_cast per accedere alle variabili e alle funzioni derivate dei membri della classe.Variabile membro membro della classe derivata accesso in funzione virtuale

Qualche soluzione migliore?

  1. In ogni stato delle classi derivate, aggiungere un altro puntatore (ad es. Convertibile * mConvertibile). Ogni stato ha puntatori duplicati (mConvertible e mVehicle) puntati sullo stesso oggetto. Non sembra giusto
  2. Utilizzare un Getter virtuale invece di mVeicolo nella classe base. Ci saranno chiamate Getter eccessive nella classe base.

============================================ ===========================

Sì. Ho provato template come sotto, ma non può essere compilato perché errori come

"car.h: nella funzione membro 'virtuale void Car :: State1 :: run()': car.h: 18: 12: errore : "mVehicle" non è stato dichiarato in questo ambito ".

// car.h 
#include <iostream> 

template <class T> 
class Car { 
public: 
    class BaseState { 
    public: 
     explicit BaseState(T* vehicle) : mVehicle(vehicle) {} 

    protected: 
     T* mVehicle; 
    }; 

    class State1 : public BaseState { 
    public: 
     explicit State1(T* vehicle) : BaseState(vehicle) {} 
     virtual void run() { 
      mVehicle->x = 1; 
      mVehicle->y = 2; 
      mVehicle->doSomething1(); 
      mVehicle->doSomething2(); 
      processEvent(); 
     } 
     virtual void processEvent() { 
      if (mVehicle->val > 2) { 
       std::cout << "too large" << std::endl; 
      } 
     } 
    }; 

    class State2 : public BaseState { 
    public: 
     explicit State2(T* vehicle) : BaseState(vehicle) {} 
     virtual void run() { 
      mVehicle->x = 10; 
      mVehicle->y = 20; 
      processEvent(); 
     } 
     virtual void processEvent() { 
      if (mVehicle->val > 20) { 
       std::cout << "too large" << std::endl; 
      } 
     } 
    }; 

    virtual void doSomething1() { 
     val += x * y; 
    } 

    virtual void doSomething2() { 
     val += x + y; 
    } 

protected: 
    int x; 
    int y; 
    int val; 

}; 

// convertible.h 
#include "car.h" 
#include <iostream> 

class Convertible : public Car<Convertible> { 
protected: 
    class State1 : public Car<Convertible>::State1 { 
     explicit State1(Convertible* vehicle) : Car<Convertible>::State1(vehicle) {} 
     // want to override functions in base class states 
     virtual void processEvent() { 
      if (mVehicle->val > 10) { 
       std::cout << "too large" << std::endl; 
       mVehicle->val = 10; 
      } 
     } 
    }; 

    // want to override some base class functions 
    // and access some special variables 
    // want to inherit other functions 
    virtual void doSomething2() { 
     z = 10; 
     val += x + y + z; 
    } 

protected: 
    int z; 
}; 

Se uso State1(Car* vehicle) invece di State1(T* vehicle), non c'è errore di conversione aggiuntivo. Che cosa sto facendo di sbagliato?

Se il programma è in grado di capire che dovrebbe essere eseguito Convertible::State1::processEvent(), perché non può lanciare automaticamente mVehicleCar*-Convertible*? Apparentemente mVehicle indica un oggetto Convertible quando viene dedotto Convertible::State1::processEvent(). Non abbiamo bisogno di modello se c'è il cast automatico.

+3

Modelli. Usali. –

+0

Potresti fornire maggiori informazioni? – dekst

+0

vedere la risposta. –

risposta

1

Questa implementazione non utilizza getta, puntatori duplicati, getter virtuali o CRTP.Ha tre gerarchie parallele:

  • automobili
  • Uniti auto astratti che sono interfacce astratte puri
  • stati auto in cemento, dove lo Stato è parametrizzato dal reale tipo run-tipo della vettura.

Quindi abbiamo per es.

Car     Car::AbstractState    Car::State<C> 
|      |         | 
+--- Convertible  +--- Convertible::AbstractState +--- Convertible::State<C> 
| |    | |       | | 
| +--- Racer  | +--- Racer::AbstractState | +--- Racer::State<C> 
+--- Hybrid   +--- Hybrid::AbstractState  +--- Hybrid::State<C> 

Ogni stato concreto deriva e implementa il corrispondente stato astratto. Se abbiamo un che punta a un Convertible e interrogiamo il suo stato, otteniamo un Car::AbstractState* che punta a un oggetto di stato concreto con il tipo massimo di Convertible::State<Convertible>. L'utente della gerarchia delle auto, tuttavia, non sa e non si preoccupa dei macchinari dei modelli.

Il codice:

#include <iostream> 
using namespace std; 

struct Trace 
{ 
    Trace(const char* s) : s (s) 
    { 
     cout << s << " start\n"; 
    } 

    ~Trace() 
    { 
     cout << s << " end\n"; 
    } 

    const char* s; 
}; 

struct Car { 
    struct AbstractState 
    { 
     virtual void run() = 0; 
    }; 

    template <typename C> 
    struct State : virtual AbstractState 
    { 
     explicit State(C* vehicle) : mVehicle(vehicle) {} 
     virtual void run() 
     { 
      Trace("Car::State::run"); 
      doSomething(); 
     }; 
     virtual void doSomething() 
     { 
      Trace("Car::State::doSomething"); 
     } 
     C* mVehicle; 
    }; 

    virtual AbstractState* getState() { return new State<Car>(this); } 
}; 


struct Convertible : Car { 

    struct AbstractState : virtual Car::AbstractState 
    { 
     virtual void runBetter() = 0; 
    }; 

    template <typename C> 
    struct State : Car::State<C>, virtual AbstractState 
    { 
     using Car::State<C>::mVehicle; 
     explicit State(C* vehicle) : Car::State<C>(vehicle) {} 
     void doSomething() 
     { 
      Trace("Convertible::State::doSomething"); 
      Car::State<C>::doSomething(); 
      mVehicle->foldTop(); 
     } 

     void runBetter() 
     { 
      Trace("Convertible::State::runBetter"); 
      run(); 
      doSomethingElse(); 
     }; 

     virtual void doSomethingElse() 
     { 
      Trace("Convertible::State::doSomethingElse"); 
     } 
    }; 

    void foldTop() 
    { 
     Trace("Convertible::foldTop"); 
    } 

    Convertible::AbstractState* getState() { return new State<Convertible>(this); } 
}; 

int main() 
{ 
    Car car; 
    Convertible convertible; 
    Car& car2(convertible); 

    cout << "runing car\n"; 
    Car::AbstractState* carstate = car.getState(); 
    carstate->run(); 

    cout << "runing convertible\n"; 
    Convertible::AbstractState* convertiblestate = convertible.getState(); 
    convertiblestate->run(); 

    cout << "runing car2\n"; 
    Car::AbstractState* carstate2 = car2.getState(); 
    carstate2->run(); 
} 
+0

Grazie mille! Un ottimo esempio per questo tipo di design. 'using Car :: State :: mVehicle;' risolve il problema che stavo avendo. – dekst

3

Utilizzare i modelli.

Rimuovi il puntatore dalle classi interne Car (le ha rese classi astratte piene di virtuali puri).

Aggiungi nuova classe template CarT (o pensare a nome migliore)

template <typename T> 
class CarT { 

class CarHolder { 
    explicit CarHolder(T* car) : car(car) {} 
    T* car; 
}; 
class State1 : public Car::State1, protected CarHolder { 
    explicit State1(Car* vehicle) : CarHolder(vehicle) {} 
    virtual void run() { 
     // use data of Car 
     ... 
     doSomething(); 
    } 
    virtual void doSomething() { 
    } 
}; 
class State2 : public Car::State2 { 
}; 
... 
}; 

In questo modo si avrà tempo di esecuzione polimorfismo della Car ed è State 's e buon polimorfismo momento della compilazione di classi derivate (che a sua volta rimuovere necessità di brutto static_cast)

class Convertible: public CarT<Convertible> { 
    typename CarT<Convertible> Base; 
    class State1 : public Base::State1 { 
     explicit State1(Convertible* vehicle) : Car::State1(vehicle) {} 
     virtual void doSomething() { 
      car->foldTop(); 
     } 
    } 
    class State2 : public Base::State2 { 
    } 
    ... 
    void foldTop() {} 
} 

class Convertible : public CarT<Convertible> potrebbe sembrare strano, ma funzionerà (CarT lo usa templ mangiato argomento solo come puntatore, se lo stava usando come membro valore ci potrebbero essere alcuni problemi)

+0

Grazie! Ho provato qualcosa di simile, ma non riesco a risolvere l'errore di compilazione. Nel tuo esempio, intendi 'class State1: public CarHolder {' invece di 'class State1: public Car :: State1 {'? Sembra che ci sia un errore di conversione da "Car *" a "Convertible *". – dekst

+0

oh, mio ​​male, dovrebbe ereditare entrambi :) codice fisso – Hcorg

Problemi correlati