首页 文章

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

提问于
浏览
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() {}
}

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

更好的解决方案?

  • 在派生类的每个状态中,添加另一个指针(例如,Convertible * mConvertible) . 每个State都有重复的指针(mConvertible和mVehicle)指向同一个对象 . 看起来不对劲 .

  • 在基类中使用虚拟Getter而不是mVehicle . 基类中会有过多的Getter调用 .

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

是 . 我尝试了如下模板,但它无法编译,因为错误就像

“car.h:在成员函数中'虚拟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 对象 . 如果有自动演员,我们不需要模板 .

2 回答

  • 3

    此实现不使用强制转换,重复指针,虚拟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();
    }
    
  • 1

    使用模板 .

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

相关问题