在虚函数中访问派生类成员变量

时间:2015-10-08 08:38:17

标签: c++ templates inheritance polymorphism

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() {}
}

所有状态都是从BaseState派生的,因此它们具有成员变量mVehicle来访问外部类变量。 但是,在每个派生类中,在每个State的所有函数中,都需要static_cast来访问派生类成员变量和函数。

有更好的解决方案吗?

  1. 在每个派生类的状态中,添加另一个指针(例如,Convertible * mConvertible)。每个State都有重复的指针(mConvertible和mVehicle)指向同一个对象。看起来不对。
  2. 在基类中使用虚拟Getter而不是mVehicle。基类中会有过多的Getter调用。
  3. =============================================== ========================

    是。我尝试了如下模板,但无法编译,因为

    之类的错误

    &#34; car.h:在成员函数'virtual void Car :: State1 :: run()'中: car.h:18:12:错误:'mVehicle'未在此范围内声明 &#34;

    // 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::State1::processEvent(),为什么不能自动将mVehicleCar*投射到Convertible*?推断出mVehicle时,显然Convertible指向Convertible::State1::processEvent()个对象。如果有自动演员,我们不需要模板。

2 个答案:

答案 0 :(得分:3)

使用模板。

Car内部类中删除指针(使它们成为充满纯虚拟的抽象类)。

添加新模板类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 {
};
...
};

通过这种方式,您将拥有Car及其State的运行时多态性以及派生类的良好编译时多态性(这反过来将消除对丑陋的需求{ {1}})

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>仅将模板参数用作指针,如果它将其用作值成员则可能存在一些问题)

答案 1 :(得分:1)

此实现不使用强制转换,重复指针,虚拟getter或CRTP。它有三个并行的层次结构:

  • 汽车
  • 抽象汽车状态是纯粹的抽象接口
  • 具体汽车状态,其中状态由汽车的实际行驶类型参数化。

所以我们有例如。

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

每个具体状态派生自并实现相应的抽象状态。如果我们有Car*指向Convertible,并且我们查询其状态,我们会得到一个Car::AbstractState*,它指向具有最终类型Convertible::State<Convertible>的具体状态对象。然而,汽车层次结构的用户不知道也不关心模板机器。

代码:

#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();
}