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

Access derived class member variable in virtual function

本文关键字:成员 变量 派生 函数 访问      更新时间:2023-10-16
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() {}
}

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

有更好的解决方案吗?

  1. 在派生类的每个State中,添加另一个指针(例如,Convertible *mConvertible)。每个State都有指向同一个对象的重复指针(mConvertible和mVehicle)。
  2. 在基类中使用虚Getter代替mVehicle。在基类中会有过多的Getter调用。

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

是的。我尝试了下面的模板,但它无法编译,因为错误如

" Car .h:在成员函数' virtual void Car::State1::run() '中:car.h:18:12:错误:' 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::State1::processEvent()应该执行,为什么它不能自动将mVehicleCar*转换为Convertible* ?显然,当Convertible::State1::processEvent()被推导出来时,mVehicle指向一个Convertible对象。如果有自动强制转换,则不需要模板。

使用模板

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和派生类的良好编译时多态性(这反过来又将消除丑陋的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>可能看起来很奇怪,但它会工作(CarT使用它的模板参数仅作为指针,如果它使用它作为值成员可能会有一些问题)

此实现不使用强制类型转换、重复指针、虚拟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>。然而,car层次结构的用户不知道也不关心模板机制。

代码:

#include <iostream>
using namespace std;
struct Trace
{
    Trace(const char* s) : s (s)
    {
        cout << s << " startn";
    }
    ~Trace()
    {
        cout << s << " endn";
    }
    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 carn";
    Car::AbstractState* carstate = car.getState();
    carstate->run();
    cout << "runing convertiblen";
    Convertible::AbstractState* convertiblestate = convertible.getState();
    convertiblestate->run();
    cout << "runing car2n";
    Car::AbstractState* carstate2 = car2.getState();
    carstate2->run();
}