2015-10-08 17 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() {} 
} 

すべての状態は、外部クラス変数にアクセスするメンバー変数mVehicleを持つように、BaseStateから派生しています。 しかし、各派生クラスでは、各状態のすべての関数において、派生クラスメンバ変数と関数にアクセスするにはstatic_castが必要です。仮想関数内の派生クラスメンバー変数へのアクセス

もっと良い解決策はありますか?

  1. 派生クラスの各状態では、別のポインタ(Convertible * mConvertibleなど)を追加します。各ステートには同じオブジェクトを指す重複ポインタ(mConvertibleとmVehicle)があります。見えません。
  2. ベースクラスでmVehicleの代わりに仮想ゲッターを使用します。ベースクラスにはゲッターコールが過剰になります。

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

はい。私は以下のようにテンプレートを試しましたが、エラーのようにエラーが発生したため、コンパイルできません。

"car.h:メンバー関数で 'virtual void Car :: State1 :: run()': car.h:18:12:error : 'mVehicle'はこのスコープで宣言されていません "

// 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; 
}; 

私はState1(Car* vehicle)代わりのState1(T* vehicle)使用する場合は、追加の変換エラーが発生しました。私は間違って何をしていますか?

プログラムが自動的にConvertible*Car*からmVehicleをキャストできない理由Convertible::State1::processEvent()は、実行する必要があることを把握することができますか? Convertible::State1::processEvent()が推定されるConvertibleオブジェクトへどうやらmVehicleポイント。自動キャストがあればテンプレートは必要ありません。

+3

テンプレート。それらを使用してください。 –

+0

詳細をお知らせください。 – dekst

+0

答えを見てください。 –

答えて

1

は、この実装にはキャスト、重複ポインタ、仮想ゲッター、またはCRTPを使用しない)いくつかの問題があるかもしれない値のメンバーとして、それを使用していた場合、唯一のポインタとして引数を食べました。

  • 車の状態は車の実際の実行型のタイプによってパラメータ化され
  • 具体的な車の状態は、純粋な抽象インターフェイスです
  • 抽象車の状態:それは3つの並列階層を持っています。

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

それぞれの具体的な状態は、対応する抽象状態から派生し、実装します。 Convertibleを指すCar*があり、状態を照会すると、最終的なタイプがConvertible::State<Convertible>の具体的な状態オブジェクトを指すCar::AbstractState*が得られます。しかし、車の階層構造のユーザーは、テンプレート機械を知らず気にもなりません。

コード:

#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

ありがとう!この種のデザインの非常に良い例です。 'Car :: State :: mVehicle;を使って、私が持っていた問題を解決してください。 – dekst

3

使用テンプレート。

Carインナークラス(彼らに純粋virtualsをの完全な抽象クラスを作った)からポインタを削除します。

順番になる(あなたがCarのランタイムポリモーフィズムを持つことになり、それがState年代だと派生クラスの良いコンパイル時のポリモーフィズムこの方法は、新しいテンプレートクラスCarTを追加(またはより良い名前を考える)

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 { 
}; 
... 
}; 

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>)醜いstatic_castの必要性を取り除く奇妙に見えるかもしれないが、それは動作します(CarTがそれを使用していますTEMPLそれは

+0

ありがとう!同様のことを試しましたが、コンパイルエラーを解決できません。あなたの例では 'class State1:​​public Car :: State1 {'?}の代わりに 'class State1:​​public CarHolder {' 'Car *'から 'Convertible *'への変換エラーがあるようです。 – dekst

+0

ああ、私の悪い、それは両方を継承する必要があります:)コード固定 – Hcorg

関連する問題